Feb 01 2019
Feb 01

In this article we will see how to update data models in Drupal 8, how to make the difference between model updating and content updating, how to create default content, and finally, the procedure to adopt for successful deployments to avoid surprises in a continuous integration/delivery Drupal cycle.

Before we start, I would encourage you to read the documentation of the hook hook_update_N() and to take into account all the possible impacts before writing an update.

Updating the database (executing hook updates and/or importing the configuration) is a very problematic task during a Drupal 8 deployment process, because the updating actions order of structure and data is not well defined in Drupal, and can pose several problems if not completely controlled.

It is important to differentiate between a contributed module to be published on drupal.org aimed at a wide audience, and a custom Drupal project (a set of Drupal contrib/custom modules) designed to provide a bespoke solution in response to a client’s needs. In a contributed module it is rare to have a real need to create instances of configuration/content entities, on the other hand deploying a custom Drupal project makes updating data models more complicated. In the following sections we will list all possible types of updates in Drupal 8.

The Field module allows us to add fields to bundles, we must make difference between the data structure that will be stored in the field (the static schema() method) and all the settings of the field and its storage that will be stored as a configuration. All the dependencies related to the configuration of the field are stored in the field_config configuration entity and all the dependencies related to the storage of the field are stored in the field_storage_config configuration entity. Base fields are stored by default in the entity’s base table.  

Configurable fields are the fields that can be added via the UI and attached to a bundle, which can be exported and deployed. Base fields are not managed by the field_storage_config configuration entities and field_config.

To update the entity definition or its components definitions (field defintions for example if the entity is fieldable) we can implement hook_update_N(). In this hook don’t use the APIs that require a full Drupal bootstrap (e.g. database with CRUD actions, services, …), to do this type of update safely we can use the methods proposed by the contract EntityDefinitionUpdateManagerInterface (e.g. updating the entity keys, updating a basic field definition common to all bundles, …)

To be able to update existing data entities or data fields in the case of a fieldable entity following a modification of a definition we can implement hook_post_update_NAME(). In this hook you can use all the APIs you need to update your entities.

To update the schema of a simple, complex configuration (a configuration entity) or a schema defined in a hook_schema() hook, we can implement hook_update_N().

In a custom Drupal project we are often led to create custom content types or bundles of custom entities (something we do not normally do in a contributed module, and we rarely do it in an installation profile), a site building action allows us to create this type of elements which will be exported afterwards in yml files and then deployed in production using Drupal configuration manager.

A bundle definition is a configuration entity that defines the global schema, we can implement hook_update_N() to update the model in this case as I mentioned earlier. Bundles are instances that persist as a Drupal configuration and follow the same schema. To update the bundles, updated configurations must be exported using the configuration manager to be able to import them into production later. Several problems can arise:

  • If we add a field to a bundle, and want to create content during the deployment for this field, using the current workflow (drush updatedb -> drush config-import) this action is not trivial, and the hook hook_post_update_NAME() can’t be used since it’s executed before the configuration import.
  • The same problem can arise if we want to update fields of bundles that have existing data, the hook hook_post_update_NAME() which is designed to update the existing contents or entities will run before the configuration is imported. What is the solution for this problem? (We will look at a solution for this problem later in this article.)

Now the question is: How to import default content in a custom Drupal project?

Importing default content for a site is an action which is not well documented in Drupal, in a profile installation often this import is done in the hook_install() hook because always the data content have not a complex structure with levels of nested references, in some cases we can use the default content module. Overall in a module we can’t create content in a hook_install() hook, simply because when installing a module the integrity of the configuration is still not imported.

In a recent project i used the drush php-script command to execute import scripts after the (drush updatedb -> drush config-import) but this command is not always available during deployment process. The first idea that comes to mind is to subscribe to the event that is triggered after the import of the configurations to be able to create the contents that will be available for the site editors, but the use of an event is not a nice developer experience hence the introduction of a new hook hook_post_config_import_NAME() that will run once after the database updates and configuration import. Another hook hook_pre_config_import_NAME() has also been introduced to fix performance issues.

A workflow that works for me

To achieve a successful Drupal deployment in continuous integration/delivery cycles using Drush, the most generic workflow that I’ve found at the moment while waiting for a deployment API in core is as follows :

  1. drush updatedb
    • hook_update_N() : To update the definition of an entity and its components
    • hook_post_update_N() : To update entities when you made an entity definition modification (entity keys, base fields, …)
  2. hook_pre_config_import_NAME() : CRUD operations (e.g. creating terms that will be taken as default values when importing configuration in the next step)
  3. drush config-import : Importing the configuration (e.g. new bundle field, creation of a new bundle, image styles, image crops, …)
  4. hook_post_config_import_NAME(): CRUD operations (e.g. creating contents, updating existing contents, …)

This approach works well for us, and I hope it will be useful for you. If you’ve got any suggestions for improvements, please let me know via the comments.

Dec 14 2018
Dec 14

A lot of people have been jumping on the headless CMS bandwagon over the past few years, but I’ve never been entirely convinced. Maybe it’s partly because I don’t want to give up on the sunk costs of what I’ve learned about Drupal theming, and partly because I’m proud to be a boring developer, but I haven’t been fully sold on the benefits of decoupling.

On our current project, we’ve continued to take an approach that Dries Buytaert has described as “progressively decoupled Drupal”. Drupal handles routing, navigation, access control, and page rendering, while rich interactive functionality is provided by a JavaScript application sitting on top of the Drupal page. In the past, we’d taken a similar approach, with AngularJS applications on top of Drupal 6 or 7, getting their configuration from Drupal.settings, and for this project we decided to use React on top of Drupal 8.

There are a lot of advantages to this approach, in my view. There are several discrete interactive applications on the site, but the bulk of the site is static content, so it definitely makes sense for that content to be rendered by the server rather than constructed in the browser. This brings a lot of value in terms of accessibility, search engine optimisation, and performance.

A decoupled system is almost inevitably more complex, with more potential points of failure.

The application can be developed independently of the CMS, so specialist JavaScript developers can work without needing to worry about having a local Drupal build process.

If at some later date, the client decides to move away from Drupal, or at the point where we upgrade to Drupal 9, the applications aren’t so tightly coupled, so the effort of moving them should be smaller.

Having made the decision to use this architecture, we wanted a consistent framework for managing application configuration, to make sure we wouldn’t need to keep reinventing the wheel for every application, and to keep things easy for the content team to manage.

The client’s content team want to be able to control all of the text within the application (across multiple languages), and be able to preview changes before putting them live.

There didn’t seem to be an established approach for this, so we’ve built a module for it.

As we’ve previously mentioned, the team at Capgemini are strongly committed to supporting the open source communities whose work we depend on, and we try to contribute back whenever we can, whether that’s patches to fix bugs and add new features, or creating new modules to fill gaps where nothing appropriate already exists. For instance, a recent client requirement to promote their native applications led us to build the App Banners module.

Aiming to make our modules open source wherever possible helps us to think in systems, considering the specific requirements of this client as an example of a range of other potential use cases. This helps to future-proof our code, because it’s more likely that evolving requirements can be met by a configuration change, rather than needing a code change.

So, guided by these principles, I’m very pleased to announce the Single Page Application Landing Page module for Drupal 8, or to use the terrible acronym that it has unfortunately but inevitably acquired, SPALP.

On its own, the module doesn’t do much other than provide an App Landing Page content type. Each application needs its own module to declare a dependency on SPALP, define a library, and include its configuration as JSON (with associated schema). When a module which does that is installed, SPALP takes care of creating a landing page node for it, and importing the initial configuration onto the node. When that node is viewed, SPALP adds the library, and a link to an endpoint serving the JSON configuration.

Deciding how to store the app configuration and make all the text editable was one of the main questions, and we ended up answering it in a slightly “un-Drupally” way.

On our old Drupal 6 projects, the text was stored in a separate ‘Messages’ node type. This was a bit unwieldy, and it was always quite tricky to figure out what was the right node to edit.

For our Drupal 7 projects, we used the translation interface, even on a monolingual site, where we translated from English to British English. It seemed like a great idea to the development team, but the content editors always found it unintuitive, struggling to find the right string to edit, especially for common strings like button labels. It also didn’t allow the content team to preview changes to the app text.

We wanted to maintain everything related to the application in one place, in order to keep things simpler for developers and content editors. This, along with the need to manage revisions of the app configuration, led us down the route of using a single node to manage each application.

This approach makes it easy to integrate the applications with any of the good stuff that Drupal provides, whether that’s managing meta tags, translation, revisions, or something else that we haven’t thought of.

The SPALP module also provides event dispatchers to allow configuration to be altered. For instance, we set different API endpoints in test environments.

Another nice feature is that in the node edit form, the JSON object is converted into a usable set of form fields using the JSON forms library. This generic approach means that we don’t need to spend time copying boilerplate Form API code to build configuration forms when we build a new application - instead the developers working on the JavaScript code write their configuration as JSON in a way that makes sense for their application, and generate a schema from that. When new configuration items need to be added, we only need to update the JSON and the schema.

Each application only needs a very simple Drupal module to define its library, so we’re able to build the React code independently, and bring it into Drupal as a Composer dependency.

The repository includes a small example module to show how to implement these patterns, and hopefully other teams will be able to use it on other projects.

As with any project, it’s not complete. So far we’ve only built one application following this approach, and it seems to be working pretty well. Among the items in the issue queue is better integration with configuration management system, so that we can make it clear if a setting has been overridden for the current environment.

I hope that this module will be useful for other teams - if you’re building JavaScript applications that work with Drupal, please try it out, and if you use it on your project, I’d love to hear about it. Also, if you spot any problems, or have any ideas for improvements, please get in touch via the issue queue.

Mar 07 2017
Mar 07

This weekend’s DrupalCamp London wasn’t my first Drupal event at all, I’ve been to 3 DrupalCon Europe, 4 DrupalCamp Dublin, and a few other DrupalCamps in Ireland and lots of meetups, but in this case I experienced a lot of ‘first times’ that I want to share.

This was the first time I’d attended a Drupal event representing a sponsor organisation, and as a result the way I experienced it was completely different.

Firstly, you focus more on your company’s goals, rather than your personal aims. In this case I was helping Capgemini UK to engage and recruit people for our open positions. This allowed me to socialise more and try to connect with people. We also had T-shirts so it was easier to attract people if you have something free for them. I was also able to have conversations with other sponsors to see why did they sponsor the event, some were also recruiting, but most of them were selling their solutions to prospective clients, Drupal developers and agencies.

The best of this experience was the people I found in other companies and the attendees approaching us for a T-shirt or a job opportunity.

New member of Capgemini UK perspective

As a new joiner in the Capgemini UK Drupal team I attended this event when I wasn’t even a month old in the company, and I am glad I could attend this event at such short notice in my new position, I think this tells a lot about the focus on training and career development Capgemini has and how much they care about Drupal.

As a new employee of the company this event allowed me to meet more colleagues from different departments or teams and meet them in a non-working environment. Again the best of this experience was the people I met and the relations I made.

I joined Capgemini from Ireland, so I was also new to the London Drupal community, and the DrupalCamp gave me the opportunity to connect and create relationships with other members of the Drupal community. Of course they were busy organising this great event, but I was able to contact some of the members, and I have to say they were very friendly when I approached any of the crew or other local members attending the event. I am very happy to have met some friendly people and I am committed to help and volunteer my time in future events, so this was a very good starting point. And again the best were the people I met.

Non-session perspective

As I had other duties I couldn’t attend all sessions. But I was able to attend some sessions and the Keynotes, with special mention to the Saturday keynote from Matt Glaman, it was very motivational and made me think anyone could evolve as a developer if they try and search the resources to get the knowledge. And the closing keynote from Danese Cooper was very inspirational as well about what Open Source is and what should be, and that we, the developers, have the power to make it happen. And we could also enjoy Malcom Young’s presentation about Code Reviews.

Conclusion

Closing this article I would like to come back to the best part of the DrupalCamp for me this year, which was the people. They are always the best part of the social events. I was able to catch up with old friends from Ireland, engage with people considering a position at Capgemini and introduce myself to the London Drupal community, so overall I am very happy with this DrupalCamp London and I will be happy to return next year. In the meantime I will be attending some Drupal meetups and trying to get involve in the community, so don’t hesitate to contact me if you have any question or you need my help.

Oct 27 2016
Oct 27

In a previous article on this blog, I talked about why code review is a good idea, and some aspects of how to conduct them. This time I want to dig deeper into the practicalities of reviewing code, and mention a few things to watch out for.

Code review is the first line of defence against hackers and bugs. When you approve a pull request, you’re putting your name to it - taking a share of responsibility for the change.

Once bad code has got into a system, it can be difficult to remove. Trying to find problems in an existing codebase is like looking for an unknown number of needles in a haystack, but when you’re reviewing a pull request it’s more like looking in a handful of hay. The difficult part is recognising a needle when you see one. Hopefully this article will help you with that.

Code review shouldn’t be a box-ticking exercise, but it can be helpful to have a list of common issues to watch out for. As well as the important question of whether the change will actually work, the main areas to consider are:

  • Security
  • Perfomance
  • Accessibility
  • Maintainability

I’ll touch on these areas in more detail - I’ll be talking about Drupal and PHP in particular, but a lot of the points I’ll make are relevant to other languages and frameworks.

Security

I don’t claim to be an expert on security, and often count myself lucky that I work in what my colleague Andrew Harmel-Law calls “a creative-inventive market, not a safety-critical one”.

Having said that, there are a few common things to keep an eye out for, and developers should be aware of the OWASP top ten list of vulnerabilities. When working with Drupal, you should bear in mind the Drupal security team’s advice for writing secure code. For me, the most important points to consider are:

Does the code accept user input without proper sanitisation?

In short - don’t trust user input. The big attack vectors like XSS and SQL injection are based on malicious text strings. Drupal provides several types of text filtering - the appropriate filter depends on what you’re going to do with the data, but you should always run user input through some kind of sanitisation.

Are we storing sensitive data anywhere we shouldn’t be?

Security isn’t just about stopping bad guys getting in where they shouldn’t. Think about what kind of data you have, and what you’re doing with it. Make sure that you’re not logging people’s private data inappropriately, or passing it across network in a way you shouldn’t. Even if the site you’re working on doesn’t have anything as sensitive as the Panama papers, you have a legal, professional, and personal responsibility to make sure that you’re handling data properly.

Performance

When we’re considering code changes, we should always think about what impact they will have on the end user, not least in terms of how quickly a site will load. As Google recently reminded us, page load speed is vital for user engagement. Slow, bloated websites cost money, both in terms of mobile data charges and lost revenue.

Does the change break caching?

Most Drupal performance strategies will talk about the value of caching. The aim of the game is to reduce the amount of work that your web server does. Ideally, the web server won’t do any work for a page request from an anonymous user - the whole thing will be handled by a reverse proxy cache, such as Varnish. If the request needs to go to the web server, we want as much of the page as possible to be served from an object cache such as Redis or Memcached, to minimise the number of database queries needed to render the page.

Are there any unnecessary uses of $_SESSION?

Typically, reverse proxy servers like Varnish will not cache pages for authenticated users. If the browser has a session, the request won’t be served by Varnish, but by the web server.

Here’s an illustration of why this is so important. This graph shows the difference in response time on a load test environment following a deployment that included some code to create sessions. There were some other changes that impacted performance, but this was the big one. As you can see, overall response time increased six-fold, with the biggest increase in the time spent by the web server processing PHP (the blue sections on the graphs), mainly because a few lines of code creating sessions had slipped through the net.

Graph showing dramatic increase in PHP evaluation time

Are there any inefficient loops?

The developers’ maxims “Don’t Repeat Yourself” and “Keep It Simple Stupid” apply to servers as well. If the server is doing work to render a page, we don’t want that work to be repeated or overly complex.

What’s the front end performance impact?

There’s no substitute for actually testing, but there are a few things that you can keep an eye out for when reviewing change. Does the change introduce any additional HTTP requests? Perhaps they could be avoided by using sprites or icon fonts. Have any images been optimised? Are you making any repeated DOM queries?

Accessibility

Even if you’re not an expert on accessibility, and don’t know ARIA roles, you can at least bear in mind a few general pointers. When it comes to testing, there’s a good checklist from the Accessibility Project, but here are some things I always try to think about when reviewing a pull request.

Will it work on a keyboard / screen reader / other input or output device ?

Doing proper accessibility testing is difficult, and you may not have access to assistive technology, but a good rule of thumb is that if you can navigate using only a keyboard, it will probably work for someone using one of the myriad input devices. Testing is the only way to be certain, but here are a couple of simple things to remember when reviewing CSS changes: hover and focus should usually go together, and you should almost never use outline: none;.

Are you hiding content appropriately?

One piece of low-hanging fruit is to make sure that text is available to screen readers and other assistive technology. Any time I see display: none; in a pull request, alarm bells start ringing. It’s usually not the right way to hide content.

Maintainability

Hopefully the system you’re working on will last for a long time. People will have to work on it in the future. You should try to make life easier for those people, not least because you’ll probably be one of them.

Reinventing the wheel

Are you writing more code than you need to? It may well be that the problem you’re looking at has already been solved, and one of the great things about open source is that you’re able to recruit an army of developers and testers you may never meet. Is there already a module for that?

On the other hand, even if there is an existing module, it might not always make sense to use it. Perhaps the contributed module provides more flexibility than our project will ever need, at a performance cost. Maybe it gives us 90% of what we want, but would force us to do things in a certain way that would make it difficult to get the final 10%. Perhaps it isn’t in a very healthy state - if so, perhaps you could fix it up and contribute your fixes back to the community, as I did on a recent project.

If you’re writing a custom module to solve a very specific problem, could it be made more generic and contributed to the community? A couple of examples of this from the Capgemini team are Stomp and Route.

One of the jobs of the code reviewer is to help draw the appropriate line between the generic and the specific. If you’re reviewing custom code, think about whether there’s prior art. If the pull request includes community-contributed code, you should still review it. Don’t assume that it’s perfect, just because someone’s given it away for nothing.

Appropriate API usage

Is your team using your chosen frameworks as they were intended? If you see someone writing a custom function to solve a problem that’s already been solved, maybe you need to share a link to the API docs for the existing solution.

Introducing notices and errors

If your logs are littered with notices about undefined variables or array indexes, not only are you likely to be suffering a performance hit from the logging, but it’s much harder to separate the signal from the noise when you’re trying to investigate something.

Browser support

Remember that sometimes, it’s good to be boring. As a reviewer, one of your jobs is to stop your colleagues from getting carried away with shiny new features like ES6, or CSS variables. Tools like Can I Use are really useful in being able to check what’s going to work in the browsers that you care about.

Code smells

Sometimes, code seems wrong. As I learned from Larry Garfield’s excellent presentation on code smells at the first Drupalcon I went to, code smells are indications of things that might be a deeper problem. Rather than re-hash the points Larry made, I’d recommend reading his slides, but it is worth highlighting some of the anti-patterns he discusses.

Functions or objects that do more than one thing

A function should have a function. Not two functions, or three. If an appropriate comment or function name includes “and”, it’s a sign you should be splitting the function up.

Functions that sometimes do different things

Another bad sign is the word “or” in the comment. Functions should always do the same thing.

Excessive complexity

Long functions are usually a sign that you might want to think about refactoring. They tend to be an indicator that the code is more complex than it needs to be. The level of complexity can be measured, but you don’t need a tool to tell you that if a function doesn’t fit on a screen, it’ll be difficult to debug.

Not being testable

Even if functions are simple enough to write tests for, do they depend on a whole system? In other words, can they be genuinely unit tested?

Lack of documentation

There’s more to be said on the subject of code comments than I can go into here, but suffice to say code should have useful, meaningful comments to help future maintainers understand it.

Tight coupling

Modules should be modular. If two parts of a system need to interact, they should have a clearly defined and documented interface.

Impurity

Side effects and global variables should generally be avoided.

Sensible naming

Is the purpose of a function or variable obvious from the name? I don’t want to rehash old jokes, but naming things is difficult, and it is important.

Why would you comment out lines of code? If you don’t need it, delete it. The beauty of version control is that you can go back in time to see what code used to be there. As long as you write a good commit message, it’ll be easy enough to find. If you think that you might need it later, put it behind a feature toggle so that the functionality can be enabled without a code release.

Specificity

In CSS, IDs and !important are the big code smells for me. They’re a bad sign that a specificity arms race has begun. Even if you aren’t going to go all the way with a system like BEM or SMACSS, it’s a good idea to keep specificity as low as possible. The excellent articles on CSS specificity by Harry Roberts and Chris Coyier are good starting points for learning more.

Standards

It’s important to follow coding standards. The point of this isn’t to get some imaginary Scout badge - code that follows standards is easier to read, which makes it easier to understand, and by extension easier to maintain. In addition, if you have your IDE set up right, it can warn you of possible problems, but those warnings will only be manageable if you keep your code clean.

Deployability

Will your changes be available in environments built by Continuous Integration? Do you need to set default values of variables which may need overriding for different environments? Just as your functions should be testable, so should your configuration changes. As far as possible, aim to make everything repeatable and automatable - if a release needs any manual changes it’s a sign that your team may need to be thinking with more of a DevOps mindset.

Keep Your Eyes On The Prize

With all this talk of coding style and standards, don’t get distracted by trivialities - it is worth caring about things like whitespace and variable naming, but remember that it’s much more important to think about whether the code actually does what it is supposed to. The trouble is that our eyes tend to fixate on those sort of things, and they cause unnecessary cognitive load.

Pre-commit hooks can help to catch coding standards violations so that reviewers don’t need to waste their time commenting on them. If you’re on a big project, it will almost certainly be worth investing some time in integrating your CI server and your code review tool, and automating checks for issues like code style, unit tests, mess detection - in short, all the things that a computer is better at spotting than humans are.

Does the code actually solve the problem you want it to? Rather than just looking at the code, spend a couple of minutes reading the ticket that it is associated with - has the developer understood the requirements properly? Have they approached the issue appropriately? If you’re not sure about the change, check out the branch locally and test it in your development environment.

Even if there’s nothing wrong with the suggested change, maybe there’s a better way of doing it. The whole point of code review is to share the benefit of the team’s various experiences, get extra eyes on the problem, and hopefully make the end product better.

I hope that this has been useful for you, and if there’s anything you think I’ve missed, please let me know via the comments.

Oct 20 2016
Oct 20

It has been a few years since I have had the opportunity to build a website from the absolute beginning. The most recent project I’m on continues in that vein, but it’s early enough for me to consider ripping it apart and starting all over again. The project is particularly interesting to me, as it’s my first opportunity to use Drupal 8 in earnest. As I’ve got an interest in automating as much as possible, I want to gain a better understanding of the configuration management features which have been introduced in Drupal 8.

Tearing it apart and starting again wasn’t the first thing considered. Being an arrogant Drupal dev, I figured I could simply poke around the GUI and rely on some things I’d seen at Drupalcon and Drupal camps in the past couple of years to see me through. I thought I would find it easy to build a replicated environment so that any new developer could come along, do a git clone, vagrant up, review a README.md file and/or wiki page and they’d be off and running.

Wrong.

This post outlines many of the things that I examined in the process of learning Drupal 8 while adopting a bit of humility. I’ve created a sample project with names changed to protect the innocent. Any comments are welcome.

The structure of the rest of this post is as follows:

Setting up and orientation with Drupal VM

I am a big fan of Jeff Geerling’s Drupal VM vagrant project, so I created a fork of it, and imaginatively called it D8config VM. We will be building a Drupal site with the standard profile which we’ll use to rapidly build a basic prototype using the Drupal GUI - no coding chops necessary. The only contributed module added and enabled is the Devel module at the start, but we will change that quickly.

Here are the prerequisites if you do follow along:

  • familiarity with the command line;
  • familiarity with Vagrant and that it’s installed on your machine (note: the tutorial requires Vagrant 1.8.1+);
  • as well as Vagrant 1.8.1+, you need to have Ansible 2.0.1+ and VirtualBox 5.0.20+ installed;
  • have installed the Vagrant Auto-network plugin with vagrant plugin install vagrant-auto_network. This will help prevent collisions with other virtual networks that may exist on your computer;
  • have installed the Vagrant::Hostsupdater plugin with vagrant plugin install vagrant-hostsupdater, which will manage the host’s /etc/hosts file by adding and removing hostname entries for you;
  • familiarity with git and GitHub;
  • if using Windows, you are comfortable with troubleshooting any issues you might come across, as it’s only been tested on a Mac;
  • familiarity with Drush.

Here is how the D8config VM differs from Drupal VM:

  • the config.yml and drupal.make.yml files have been committed, unlike the normal Drupal VM repo;
  • the hostname and machine name have been changed to d8config.dev and d8config respectively;
  • to take advantage of the auto-network plugin, vagrant_ip is set to 0.0.0.0. The d8config machine will then have an IP address from 10.20.1.2 to 10.20.1.254;
  • the first synced folder is configured with a relative reference to the Vagrant file itself:
# The first synced folder will be used for the default Drupal installation, if
# build_makefile: is 'true'.
- local_path: ../d8config-site         # Changed from ~/Sites/drupalvm
  destination: /var/www/d8config-site  # Changed from /var/www/drupalvm
  type: nfs
  create: true

I’ll do the same with subsequent shared folders as we progress - it’s a useful way to keep the different repos together in one directory. At the end of the tutorial, you’ll have something like:

└── projects
    ├── another_project
    ├── d8config
    │   ├── d8config_profile
    │   ├── d8config-site
    │   └── d8config-vm
    ├── my_project
    └── top_secret_project

top

New nice-to-haves (thanks to recent changes in Drupal VM)

If you have used Drupal VM before but haven’t upgraded in a while, there are a load of new features. Here are just two to note:

  • Ansible roles are now installed locally (in ./provisioning/roles) during the first vagrant up;
  • PHP 7 is now an option to be installed. In fact, it’s installed by default. You can select 5.6 if you like by changing php_version in the config.yml file (see PHP 5.6 on Drupal VM).

Now do this

Create a directory where you’re going to keep all of the project assets.

$ mkdir d8config

# Change to that directory in your terminal:
$ cd d8config

Clone the D8config VM repo. Or, feel free to fork D8config VM and clone your version of the repo.

$ git clone https://github.com/siliconmeadow/d8config-vm.git

# Change to the `d8config-vm` directory:
$ cd d8config-vm

# Checkout the `CG01` branch of the repo:
$ git checkout CG01

# Bring the vagrant machine up and wait for the provisioning to complete.
$ vagrant up

After successful provisioning you should be able to point your browser at http://d8config.dev and see your barebones Drupal 8 site. Username: admin; Password: admin.

Caveats

If you’ve used Drupal VM before, you will want to examine the changes in the latest version. From Drupal VM tag 3.0.0 onwards, the requirements have changed:

  • Vagrant 1.8.1+
  • Ansible 2.0.1+
  • VirtualBox 5.0.20+

One sure sign that you’ll need to upgrade is if you see this message when doing a vagrant up:

ansible provisioner: * The following settings shouldn't exist: galaxy_role_file

top

Build your prototype

In this section of the tutorial we’re going to start building our prototype. The brief is:

The site is a portfolio site for for a large multinational corporation’s internal use. But hopefully the content architecture is simple enough to keep in your head. The following node types need to be set up: Case study, Client, Team member (the subject matter expert), and Technologies used. Set up a vocabulary called Country for countries served and a second to called Sector classify the information which will contain tags such as Government, Music industry, Manufacturing, Professional services, etc. Delete the existing default content types. You can then delete fields you know you won’t need to avoid confusion - Comments for example - which will then allow you to uninstall the comment module. And, as you might deduce by the modules I’ve selected, it’s to be a multilingual site.

Hopefully this should feel comfortable enough for you, if you are familiar with Drupal site building. There are enough specifics for clarity, yet it’s not too prescriptive that you feel someone is telling you how to do your job. Whereas in one context, you may hear a manager say “don’t bring me problems, bring me solutions”, most engineers would rather say for themselves “don’t bring me solutions, bring me problems”. I hope this brief does the latter.

Have a go at making the changes to your vanilla Drupal 8 site based on the brief.

Beyond the brief

Every ‘site building’ exercise with Drupal is a move further away from the configuration provided by the standard or minimal profiles. In our circumstance, we will enable these modules via the GUI:

  • Responsive Image
  • Syslog
  • Testing
  • BigPipe
  • Devel Generate (Devel was installed due to settings in config.yml in the d8config-vm repo)
  • Devel Kint
  • Devel Node Access
  • Web Profiler
  • Configuration Translation
  • Content Translation
  • Interface Translation
  • Language

I’ve also added a couple of contributed themes via Drush so the site will no longer look like the default site.

# While in your d8config-vm directory:
$ vagrant ssh

# Switch to your Drupal installation:
$ cd /var/www/d8config-site/drupal

# Download and install two themes:
$ drush en integrity adminimal_theme -y

For more details on these themes, see the Integrity theme and the Adminimal theme. As you might expect, I set Integrity as the default theme, and Adminimal as the admin theme via the GUI.

After switching themes, two blocks appeared in the wrong regions. I went to the Block layout page and moved the Footer menu block from the main menu region to the footer first region and the Powered by Drupal block from the Main menu to the Sub footer block.

Due to the multilingual implication, I went to the Languages admin page and added French.

top

Replicate and automate

At this stage you’ve made quite a lot of changes to a vanilla Drupal site. There are many reasons you should consider automating the building of this site - to save time when bringing other members into the development process, for creating QA, UAT, pre-prod and production environments, etc. We will now start to examine ways of doing just this.

drush make your life easier

In this section we’re going to create a Drush makefile to get the versions of Drupal core, contrib modules and themes we need to build this site as it currently is. This file will be the first file added to the D8config profile repo. Makefiles are not a required part of a profile, and could reside in a repo of their own. However to keep administration down to a minimum, I’ve found that this is a useful way to simplify some of the asset management for site building.

Let’s first tweak the config.yml in the D8config VM repo, so that we have synced folder for the profile. To do so, either:

  1. git checkout CG02 in the d8config-vm directory (where I’ve already made the changes for you), or;
  2. Add the following to the config.yml in the vagrant_synced_folders section:
# This is so the profile repo can be manipulated on the guest or host.
- local_path: ../d8config_profile
  destination: /build/d8config/d8config_profile
  type: nfs
  create: true

After doing either of the above, do a vagrant reload which will both create the directory on the Vagrant host, and mount it from the d8config-vm guest.

Next, let’s generate a basic makefile from the site as it now is.

# From within the d8config vm:
$ cd /var/www/d8config-site/drupal
$ drush generate-makefile /build/d8config/d8config_profile/d8config.make

This makefile is now available in the d8config_profile directory which is at the same level as your d8config-vm directory when viewing on your host machine.

Because we only have Drupal core, two contrib themes and the Devel module, it’s a very simple file and it doesn’t need any tweaking at this stage. I’ve committed it to the D8config profile repo and tagged it as CG01.

Raising our profile

Since we’ve established that the makefile is doing very little on this site, we need to look at completing the rest of the profile which will apply the configuration changes when building the site. The How to Write a Drupal 8 Installation Profile is quite clear and we’ll use that page to guide us.

First, our machine name has already been chosen, as I’ve called the repo d8config_profile.

Rather than writing the d8config_profile.info.yml file from scratch, let’s duplicate standard.info.yml from the standard profile in Drupal core, as that’s what we used to build the vanilla site to begin with. We can then modify it to reflect what we’ve done since.

# From within the /build/d8config/d8config_profile directory in the vagrant machine:
$ cp /var/www/d8config-site/drupal/core/profiles/standard/standard.info.yml .

$ mv standard.info.yml d8config_profile.info.yml

The first five lines of the d8config_profile.info.yml need to look like this:

name: D8config
type: profile
description: 'For a Capgemini Engineering Blog tutorial.'
core: 8.x
dependencies:

At the end of the file it looks like this, which shows the required core modules and adding the modules and themes we’ve downloaded:

- automated_cron
- responsive_image
- syslog
- simpletest
- big_pipe
- migrate
- migrate_drupal
- migrate_drupal_ui
- devel
- devel_generate
- kint
- devel_node_access
- webprofiler
- config_translation
- content_translation
- locale
- language
themes:
- bartik
- seven
- integrity
- adminimal_theme

Also, don’t forget, we uninstalled the comment module, so I’ve also removed that from the dependencies.

You still need moar!

The profile specifies the modules to be enabled, but not how they’re to be configured. Also, what about the new content types we’ve added? And the taxonomies? With previous versions, we relied on the features module, and perhaps strongarm to manage these tasks. But now, we’re finally getting to the subject of the tutorial - Drupal 8 has a configuration system out of the box.

This is available via the GUI, as well as Drush. Either method allows you to export and import the configuration settings for the whole of your site. And if you look further down the profile how-to page, you will see that we can include configuration with installation profiles.

Let’s export our configuration using Drush. This is will be far more efficient than exporting via the GUI, which downloads a *.tar.gz file, which we’d need to extract a copy or move to the config/install directory of the profile.

While logged into the vagrant machine and inside the site’s root directory:

# Create the config/install directory first:
$ mkdir -p /build/d8config/d8config_profile/config/install

# Export!
$ drush config-export --destination="/build/d8config/d8config_profile/config/install"

When I exported my configuration, there were ~215 files created. Try ls -1 | wc -l in the config/install directory to check for yourself.

top

The reason we’re gathered here today (a brief intermission)…

I hope you are finding this tutorial useful - and also sensible. When I started writing this blog post, I hadn’t realised it would cover quite so much ground. The key thing I thought I would be covering was Drupal 8’s configuration management. It was something I was very excited about, and I still am. To demonstrate some of the fun I’ve had with it is still the central point of this blog. All of the previous steps to get to this point were fun too, don’t get me wrong. From my point of view, there were no surprises.

Spoiler alert

Configuration management, on the other hand - this is true drama. Taking an existing shared development site and recreating it locally using Drush make and a basic profile (without the included config/install directory) is just a trivial soap opera. If you want real fun, visit the configuration-syncing aspect, armed only with knowledge of prior versions of Drupal and don’t RTFM.

Do RTFM

No, really. Do it.

The secret sauce in this recipe is…

After doing the export of the configuration in the previous section, I finally started running into the problems that I faced during my real world project - the project mentioned at the beginning of this post. Importing the configuration repeatedly and consistently failed with quite noisy and complex stack trace errors which were difficult to make sense of. Did I mention that perhaps I should have read the manual?

We need to do two things to make the configuration files usable in this tutorial before committing:

# Within the d8config_profile/config/install directory:
$ rm core.extension.yml update.settings.yml
$ find ./ -type f -exec sed -i '/^uuid: /d' {} \;

The removal of those two files was found to be required thanks to reading this and this. At this stage, I can confirm these were the only two files necessary for removal, and perhaps as Drupal 8’s configuration management becomes more sophisticated, this will not be necessary. The second command will recursively remove the lines with the uuid key/value pairs in all files.

top

Packaging it all up and running with it.

We’ve done all the preparation, and now need to make some small tweaks and commit them so our colleagues can start where we’ve left off. To do so we need to:

  1. add the profile to the makefile;
  2. commit our changes to the d8config_profile repo;
  3. tweak the config.yml file in the d8config-vm repo, to use our makefile and profile during provisioning.

To have the profile be installed by the makefile, add this to the bottom of d8config.make (in the D8config profile):

d8config_profile:
  type: profile
  download:
    type: git
    url: [email protected]:siliconmeadow/d8config_profile.git
    working-copy: true

I’ve committed the changes to the D8Config profile and tagged it as CG02.

Then the last change to make before testing our solution is to tweak the config.yml in the D8config VM repo. Three lines need changing:

# Change the drush_makefile_path:
drush_makefile_path: "/build/d8config/d8config_profile/d8config.make"

# Change the drupal_install_profile:
drupal_install_profile: d8config_profile

# Remove devel from the drupal_enable_modules array:
drupal_enable_modules: []

As you can see, the changes to the vagrant project are all about the profile.

With both the D8Config VM and the D8Config profile in adjacent folders, and confident that this is all going to work, from the host do:

# From the d8config-vm directory
$ vagrant destroy
# Type 'y' when prompted.

# Go!
$ vagrant up

Once the provisioning is complete, you should be able to check that the site is functioning at http://d8config.dev. Once there, check the presence of the custom content types, taxonomy, expected themes, placement of blocks, etc.

top

Summary and conclusion

The steps we’ve taken in this tutorial have given us an opportunity to look at the latest version of Drupal VM, build a quick-and-dirty prototype in Drupal 8 and make a profile which our colleagues can use to collaborate with us. I’ve pointed out some gotchas and in particular some things you will want to consider regarding exporting and importing Drupal 8 configuration settings.

There are more questions raised as well. For example, why not simply keep the d8config.make file in the d8config-vm repo? And what about the other ways people use Drupal VM in their workflow - for example here and here? Why not use the minimal profile when starting a protoype, and save the step of deleting content types?

Questions or comments? Please let me know. And next time we’ll just use Docker, shall we?

Sep 18 2016
Sep 18

If you’re migrating from a different CMS platform, the advantages of Drupal 8 seem fairly clear. But what if you’re already on Drupal? There has been a lot of discussion in the Drupal community lately about upgrading to Drupal 8. When is the right time? Now that the contributed module landscape is looking pretty healthy, there aren’t many cases where I’d recommend going with Drupal 7 for a new project. However, as I’ve previously discussed on this blog, greenfield projects are fairly rare.

Future proofing

One of the strengths of an open source project like Drupal is the level of support from the community. Other people are testing your software, and helping to fix bugs that you might not have noticed. Drupal 7 will continue to be supported until Drupal 9 is released, which should be a while away yet. However, if your site is on Drupal 6, there are security implications of remaining on an unsupported version, and it would be wise to make plans to upgrade sooner rather than later, even with the option of long term support. While the level of support from the community will no longer be the same, sites built on older versions of Drupal won’t suddenly stop working, and there are still some Drupal 5 sites out there in the wild.

Technical debt

Most big systems could do with some refactoring. There’s always some code that people aren’t proud of, some decisions that were made under the pressure of a tight deadline, or just more modern ways of doing things.

An upgrade is a great opportunity to start with a blank piece of paper. Architectural decisions can be revisited, and Drupal 8’s improved APIs are ideal if you’re hoping to take a more microservices-oriented approach, rather than ending up with another MySQL monolith.

Drupal’s policy of backward incompatibility means that while you’re upgrading the CMS, you have the chance to refactor and improve the existing custom codebase (but don’t be suckered in by the tempting fallacy that you’ll be able to do a perfect refactoring).

There are no small changes

Don’t underestimate how big a job upgrading will be. At the very least, every custom module in the codebase will need to be rewritten for Drupal 8, and custom themes will need to be rebuilt using the Twig templating system. In a few cases, this will be a relatively trivial job, but the changes in Drupal 8 may mean that some modules will need to be rebuilt from the ground up. It isn’t just about development - you’ll need to factor in the time it will take to define requirements, not to mention testing and deployment. If it’s a big project, you may also need to juggle the maintenance of the existing codebase for some time, while working on the new version.

The sites that we tend to deal with at Capgemini are big. We work with large companies with complex requirements, a lot of third party integrations, and high traffic. In other words, it’s not just your standard brochureware, so we tend to have a lot of custom modules.

If it ain’t broke, don’t fix it

Given the fact that an upgrade is non-trivial, the question has to be asked - what business value will an upgrade bring? If all you’re doing is replacing a Drupal 7 site with a similar Drupal 8 site, is it really a good idea to spend a lot of time and money to build something that is identical, as far as the average end user can tell?

If the development team is focused on upgrading, will there be any bandwidth for bug fixes and improvements? An upgrade will almost certainly be a big investment - maybe that time, energy and money would be better spent on new features or incremental improvements that will bring tangible business value and can be delivered relatively quickly. Besides, some of the improvements in Drupal 8 core, such as improved authoring experience, are also available in the Drupal 7 contrib ecosystem.

On the other hand, it might make more sense to get the upgrade done now, and build those improvements on top of Drupal 8, especially if your existing codebase needs some TLC.

Another option (which we’ve done in the past for an upgrade from Drupal 6 to 7) is to incrementally upgrade the site, releasing parts of the new site as and when they’re ready.

The right approach depends on a range of factors, including how valuable your proposed improvements will be, how urgent they are, and how long an upgrade will take, which depends on how complex the site is.

The upside of an upgrade

Having said all of that, the reasons to upgrade to Drupal 8 are compelling. One big plus for Drupal 8 is the possibility of improved performance, especially for authenticated users, thanks to modern features like BigPipe. The improved authoring experience, accessibility and multilingual features that Drupal 8 brings will be especially valuable for larger organisations.

Not only that, improving Developer Experience (DX) was a big part of the community initiatives in building Drupal 8. Adopting Symfony components, migrating code to object-oriented structures, improving the APIs and a brand new configuration management system are all designed to improve developer productivity and code quality - after the initial learning curve. These improvements will encourage more of an engineering mindset, and drive modern development approaches. The net benefit will be more testable (and therefore more reliable) features, easier deployment and maintenance methods and increase speed of future change.

Decision time

There is no one-size-fits-all answer. Your organisation will need to consider its own situation and needs.

Where does upgrading the CMS version fit into the organisation’s wider digital roadmap? Is there a site redesign on the cards any time soon? What improvements are you hoping to make? What functionality are you looking to add? Does your site’s existing content strategy meet your needs? Is the solution architecture fit for your current and future purposes, or would it make sense to think about going headless?

In summary, while an upgrade will be a big investment, it may well be one that is worth making, especially if you’re planning major changes to your site in the near future.

If the requirements for your upgrade project are “build us the same as what we’ve got already, but with more modern technology” then it’s probably not going to be worth doing. Don’t upgrade to Drupal 8 just because it’s new and shiny. However, if you’re looking further forward and planning to build a solid foundation for future improvements then an upgrade could be a very valuable investment.

Jun 23 2016
Jun 23

These days, it’s pretty rare that we build websites that aren’t some kind of redesign. Unless it’s a brand new company or project, the client usually has some sort of web presence already, and for one reason or another, they’ve decided to replace it with something shiny and new.

In an ideal world, the existing system has been built in a sensible way, with a sound content strategy and good separation of concerns, so all you need to do is re-skin it. In the Drupal world, this would normally mean a new theme, or if we’re still in our dream Zen Garden scenario, just some new CSS.

However, the reality is usually different. In my experience, redesigns are hardly ever just redesigns. When a business is considering significant changes to the website like some form of re-branding or refresh, it’s also an opportunity to think about changing the content, or the information architecture, or some aspects of the website functionality. After all, if you’re spending time and money changing how your website looks, you might as well try to improve the way it works while you’re at it.

So the chances are that your redesign project will need to change more than just the theme, but if you’re unlucky, someone somewhere further back along the chain has decided that it’s ‘just a re-skinning’, and therefore it should be a trivial job, which shouldn’t take long. In the worst case scenario, someone has given the client the impression that the site just needs a new coat of paint, but you’re actually inheriting some kind of nasty mess with unstable foundations that should really be fixed before you even think about changing how it looks. Incidentally, this is one reason why sales people should always consult with technical people who’ve seen under the bonnet of the system in question before agreeing prices on anything.

Even if the redesign is relatively straightforward from a technical point of view, perhaps it’s part of a wider rebranding, and there are associated campaigns whose dates are already expensively fixed, but thinking about the size of the website redesign project happened too late.

In other words, for whatever reason, it’s not unlikely that redesign projects will find themselves behind schedule, or over budget - what should you do in this situation? The received agile wisdom is that time and resources are fixed, so you need to flex on scope. But what’s the minimum viable product for a redesign? When you’ve got an existing product, how much of it do you need to rework before you put the new design live?

This is a question that I’m currently considering from a couple of angles. In the case of one of my personal projects, I’m upgrading an art gallery listings site from Drupal 6 to Drupal 8. The old site is the first big Drupal site I built, and is looking a little creaky in places. The design isn’t responsive, and the content editing experience leaves something to be desired. However, some of the contributed modules don’t have Drupal 8 versions yet, and I won’t have time to do the work involved to help get those modules ready, on top of the content migration, the new theme, having a full-time job and a family life, and all the rest of it.

In my day job, I’m working with a large multinational client on a set of sites where there’s no Drupal upgrade involved, but the suggested design does include some functional changes, so it isn’t just a re-theming. The difficulty here is that the client wants a broader scope of change than the timescales and budget allow.

When you’re in this situation, what can you do? As usual with interesting questions, the answer is ‘it depends’. Processes like impact mapping can help you to figure out the benefits that you get from your redesign. If you’ve looked at your burndown rates, and know that you’re not going to hit the deadline, what can you drop? Is the value that you gain from your redesign worth ditching any of the features that won’t be ready? To put it another way, how many of your existing features are worth keeping? A redesign can (and should) be an opportunity for a business to look at their content strategy and consider rationalising the site. If you’ve got a section on your site that isn’t adding any value, or isn’t getting any traffic, and the development team will need to spend time making it work in the new design, perhaps that’s a candidate for the chop?

We should also consider the Pareto principle when we’re structuring our development work, and start by working on the things that will get us most of the way there. This fits in with an important point made by scrum, which can sometimes get forgotten about: that each sprint should deliver “a potentially shippable increment”. In this context, I would interpret this to mean that we should make sure that the site as a whole doesn’t look broken, and then we can layer on the fancy bits afterwards, similar to a progressive enhancement approach to dealing with older browsers. If you aren’t sure whether you’ll have time to get everything done, don’t spend an excessive amount of time polishing one section of the site to the detriment of basic layout and styling that will make the whole site look reasonably good.

Starting with a style guide can help give you a solid foundation to build upon, by enabling you to make sure that all the components on the site look presentable. You can then test those components in their real contexts. If you’ve done any kind of content audit (and somebody really should have done), you should have a good idea of the variety of pages you’ve got. At the very least, your CMS should help you to know what types of content you have, so that you can take a sample set of pages of each content type or layout type, and you’ll be able to validate that they look good enough, whatever that means in your context.

There is another option, though. You don’t have to deliver all the change at once. Can you (and should you) do a partial go-live with a redesign? Depending on how radical the redesign is, the attitudes to change and continuous delivery within your organisation and client, and the technology stack involved, it may make sense to deliver changes incrementally. In other words, put the new sections of the site live as they’re ready, and keep serving the old bits from the existing system. There may be brand consistency, user experience, and content management process reasons why you might not want to do this, but it is an option to consider, and it can work.

On one previous project, we were carrying out a simultaneous redesign and Drupal 6 to 7 upgrade, and we were able to split traffic between the old site and the new one. It made things a little bit more complicated in terms of handling user sessions, but it did give the client the freedom to decide when they thought we had enough of the new site for them to put it live. In the end, they decided that the answer was ‘almost all of it’.

So what’s the way forward?

In the case of my art gallery listings site, the redesign itself has a clear value, and with Drupal 6 being unsupported, I need to get the site onto Drupal 8 sooner rather than later. There’s definitely a point that will come fairly soon, even if I don’t get to spend as long as I’d like working on it, where the user experience will be improved by the new site, even though some of the functionality from the old site isn’t there, and isn’t likely to be ready for a while. I’m my own client on that project, so I’m tempted to just put the redesign live anyway.

In the case of my client, there are decisions to be made about which of the new features need to be included in the redesign. De-scoping some of the more complex changes will bring the project back into the realm of being a re-theming, the functional changes can go into subsequent releases, and hopefully we’ll hit the deadline.

A final point that I’d like to make is that we shouldn’t fall into the trap of thinking of redesigns as big-bang events that sit outside the day-to-day running of a site. Similarly, if you’re thinking about painting your house, you should also think about whether you also need to fix the roof, and when you’re going to schedule the cleaning. Once the painting is done, you’ll still be living there, and you’ll have the opportunity to do other jobs if and when you have the time, energy, and money to do so.

Along with software upgrades, redesigns should be considered as part of a business’s long-term strategy, and they should be just one part of a plan to keep making improvements through continuous delivery.

Aug 26 2015
Aug 26

Based on my presentations at DrupalCamp London, on Saturday 28th February 2015 and DrupalCamp Bristol, July 4th 2015

Concept of a field

Fields are the data entry points to a web application. Usually, they provide HTML elements and may be responsible for any manipulation of data before it goes into and comes out of the application. The data captured from a single field can be simple or complex.

Assuming we want to create a field for country, all we need is a single HTML element - textfield or select options. An address field, on the other hand, is a collection of discrete data which may include standalone simple fields including a textfield representing postcode (provided by a core module) and a country field (maybe from a custom or contributed module).

Screenshot of simple and complex fields

In the Drupal world, when considering solutions the first phrase you may hear is, “there’s a module for that!”. However, for the task at hand, “you need a module for that!”. We are now going to write a module which provides this custom country field..

To create a new field in Drupal 7, a number of hooks need to be implemented in a custom module and these include the following:

  • hook_field_info() - the field type definition as well as its settings.
  • hook_field_schema() - the database schema for the field structure.
  • hook_field_widget_info() - the widget types to use for the field type.
  • hook_field_formatter_info() - the display of field values.

The task in Drupal 8 is founded on the same principles although the implementation differs. The first thing to remember here is, “there is a class for that!”. A lot of the hard work has been done in Drupal core and all we need to do is extend some classes and override default methods to suit our implementation.

Comparison of Drupal 7 hooks with Drupal 8 plugins

Creating a module

All contrib and custom modules should be placed inside the “modules” folder in your site root. Core-related code is now in “core”. However, it’s also best practice to have “contrib” and “custom” sub-folder in “modules” for clear separation of these types of modules. So we’ll create our “country” folder under modules\custom. What used to go inside *.info file is now in country.yml, so we create that file too and add the following:

name: Country
type: module
description: Defines a simple country field type.
package: Field types
version: VERSION
core: 8.x
dependencies:
  - field

Inside your module directory, you need a “src” subdirectory to keep all your class files. To organise things further, you need a “Plugin” sub-folder in “src”. There are different types of plugins e.g. fields, actions, blocks and menus. So you need to create another folder called “Field” inside Plugin and you’ll end up with a directory structure like src\Plugin\Field

Example directory structure for a Drupal 8 module

Next, we need to define our data type, widget and formatter. These will be in classes with each occupying its own folder again. Let’s take them one by one.

Data Type

The folder is called FieldType, so create it - src\Plugin\Field\FieldType. Create a class, which we shall call “CountryItem”. The file is called CountryItem.php and in it we should have:

class CountryItem {
}

How do we define our field type? With the new plugin system, this requires an annotation1 - something like a special comment block to allow core classes know about our field type. Your code should now look like this:

/**
 * Plugin implementation of the 'country' field type.
 *
 * @FieldType(
 *   id = "country",
 *   label = @Translation("Country"),
 *   description = @Translation("Stores the ISO-2 name of a country."),
 *   category = @Translation("Custom"),
 *   default_widget = "country_default",
 *   default_formatter = "country_default"
 * )
 */
class CountryItem {
}

The information provided in our annotation is quite similar to that provided when implementing hook_field_info() in Drupal 7. Next, at the top of our file, we add a few things like namespaces and import required core classes.

namespace Drupal\country\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\Field\FieldStorageDefinitionInterface;

Then we make our class inherit from core FieldItem class by extending it.

class CountryItem extends FieldItemBase {
}

There are two functions we must implement in our class - schema() and propertyDefinitions(). They’re as follows:

public static function schema(FieldStorageDefinitionInterface $field_definition) {
    return array(
      'columns' => array(
        'value' => array(
          'type' => 'char',
          'length' => static::COUNTRY_ISO2_MAXLENGTH,
          'not null' => FALSE,
        ),
      ),
      'indexes' => array(
        'value' => array('value'),
      ),
    );
  }

Here we define the schema for this field. The column is to be called “value”, and will hold a 2-character string, representing the ISO-2 name of countries. Oh, don’t forget to add the constant for the length in your class:

const COUNTRY_ISO2_MAXLENGTH = 2;
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['value'] = DataDefinition::create('string')
      ->setLabel(t('Country'));
    return $properties;
  }

However, we need to add two methods to make our life easier. Firstly, we want to know when our field is considered empty which is what hook_field_is_empty() does in Drupal 7. Then, we want to add some validation so that the country code we want to store doesn’t exceed the maximum length we have defined for our schema. When we are through, our class should look like this:

/**
 * @file
 * Contains \Drupal\country\Plugin\field\field_type\CountryItem.
 */

namespace Drupal\country\Plugin\Field\FieldType;

use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\Field\FieldStorageDefinitionInterface;

/**
 * Plugin implementation of the 'country' field type.
 *
 * @FieldType(
 *   id = "country",
 *   label = @Translation("Country"),
 *   description = @Translation("Stores the ISO-2 name of a country."),
 *   category = @Translation("Custom"),
 *   default_widget = "country_default",
 *   default_formatter = "country_default"
 * )
 */
class CountryItem extends FieldItemBase {

  const COUNTRY_ISO2_MAXLENGTH = 2;

  /**
   * {@inheritdoc}
   */
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['value'] = DataDefinition::create('string')
      ->setLabel(t('Country'));
    return $properties;
  }

  /**
   * {@inheritdoc}
   */
  public static function schema(FieldStorageDefinitionInterface $field_definition) {
    return array(
      'columns' => array(
        'value' => array(
          'type' => 'char',
          'length' => static::COUNTRY_ISO2_MAXLENGTH,
          'not null' => FALSE,
        ),
      ),
      'indexes' => array(
        'value' => array('value'),
      ),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function isEmpty() {
    $value = $this->get('value')->getValue();
    return $value === NULL || $value === '';
  }

  /**
   * {@inheritdoc}
   */
  public function getConstraints() {
    $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
    $constraints = parent::getConstraints();
    $constraints[] = $constraint_manager->create('ComplexData', array(
      'value' => array(
        'Length' => array(
          'max' => static::COUNTRY_ISO2_MAXLENGTH,
          'maxMessage' => t('%name: the country iso-2 code may not be longer than @max characters.', array('%name' => $this->getFieldDefinition()->getLabel(), '@max' => static::COUNTRY_ISO2_MAXLENGTH)),
        )
      ),
    ));
    return $constraints;
  }
}

Data Input

Now we have defined our data type and we want to store the ISO-2 country code. How do we want users to input data? We have two options - select dropdown options and an autocomplete textfield. The select options can be the default widget.

We start by creating a class called CountryDefaultWidget in src\Plugin\Field\FieldWidget\CountryDefaultWidget.php with the following code:

namespace Drupal\country\Plugin\Field\FieldWidget;

use Drupal;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;

class CountryDefaultWidget extends WidgetBase {
}

There’s still an important thing missing from our widget class - annotation of the class as provider of a FieldWidget. Add this just above the class statement;

/**
 * Plugin implementation of the 'country_default' widget.
 *
 * @FieldWidget(
 *   id = "country_default",
 *   label = @Translation("Country select"),
 *   field_types = {
 *     "country"
 *   }
 * )
 */

This is similar to the old array keys for the old hook_widget_info() in Drupal 7. Additional annotation keys may be defined by a hook_field_widget_info_alter() function.

Our CountryDefaultWidget class isn’t complete yet. Widgets handle how fields are displayed in edit forms. The missing method we need to implement will do this for us. Add this formElement() method:

public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $countries = \Drupal::service('country_manager')->getList();
    $element['value'] = $element + array(
        '#type' => 'select',
        '#options' => $countries,
        '#empty_value' => '',
        '#default_value' => (isset($items[$delta]->value) && isset($countries[$items[$delta]->value])) ? $items[$delta]->value : NULL,
        '#description' => t('Select a country'),
      );
    return $element;
}

Other modules may alter the form element provided by this function using hook_field_widget_form_alter() or hook_field_widget_WIDGET_TYPE_form_alter().

This default country widget is a simple widget of select options. Drupal core provides a list of all countries in the world as an array of country names keyed by ISO-2 country codes. This is made available as a service for use anywhere in a Drupal project.

Let’s start off with a complete implementation for this. Create src\Plugin\Field\FieldWidget\CountryAutocompleteWidget.php with this code:

namespace Drupal\country\Plugin\Field\FieldWidget;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal;
use Drupal\Core\Form\FormStateInterface;

/**
 * Plugin implementation of the 'country_autocomplete' widget.
 *
 * @FieldWidget(
 *   id = "country_autocomplete",
 *   label = @Translation("Country autocomplete widget"),
 *   field_types = {
 *     "country"
 *   }
 * )
 */
class CountryAutocompleteWidget extends WidgetBase {
}

There’s nothing unusual here at all. We need to implement same defaultSettings() and formElement() methods as for the default widget. Add this to the class:

public static function defaultSettings() {
    return array(
      'size' => '60',
      'autocomplete_route_name' => 'country.autocomplete',
      'placeholder' => '',
    ) + parent::defaultSettings();
  }

We want a textfield that’s wide enough (‘size’ => 60). For the autocomplete_route_name key we provide the name of the route from which our autocomplete functionality will return matching values. We’ll be implementing that shortly. We don’t want anything as the placeholder.

Finally, let’s add our formElement() method:

public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
    $countries = \Drupal::service('country_manager')->getList();
    $element['value'] = $element + array(
      '#type' => 'textfield',
      '#default_value' =>  (isset($items[$delta]->value) && isset($countries[$items[$delta]->value])) ? $countries[$items[$delta]->value] : '',
      '#autocomplete_route_name' => $this->getSetting('autocomplete_route_name'),
      '#autocomplete_route_parameters' => array(),
      '#size' => $this->getSetting('size'),
      '#placeholder' => $this->getSetting('placeholder'),
      '#maxlength' => 255,
      '#element_validate' => array('country_autocomplete_validate'),
    );
    return $element;
  }

This is a standard autocomplete widget. Looking at the FAPI array keys, some are very familiar. #autocomplete_route_name matches what we entered in our defaultSettings() a while ago. The value is retrieved from there with $this->getSetting(‘autocomplete_route_name’). The same goes for #size and #placeholder. Our #autocomplete_route_parameters has no default value. In order to ensure that the final value to be submitted doesn’t include unwanted values, we add #element_validate and enter the name of our callback function. We will also implement this shortly.

Creating a route

Create a YAML configuration file called country.routing.yml in your main module directory with the following:

country.autocomplete:
  path: '/country/autocomplete'
  defaults:
    _controller: '\Drupal\country\Controller\CountryAutocompleteController::autocomplete'
  requirements:
    _permission: 'administer content types'

The key or name of the route is country.autocomplete which is how this route will be referred to anywhere in the application. At least a route should define 3 things: path, code to execute and who can access the path.

  • path: This is the URL for our AJAX autocomplete calls.
  • _controller: This is the code we want to execute when we visit the defined path. It’s written as CLASS::FUNCTION. If our function requires any parameters, this is where they will be specified. We will be creating our controller shortly.
  • _permission: The string representation of the permission for access control

Now we move on to the creation of our controller class. Create a folder called Controller under src. Then add CountryAutocompleteController.php inside it. Add this code:

/**
 * @file
 * Contains \Drupal\country\Controller\CountryAutocompleteController.
 */

namespace Drupal\country\Controller;

use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Component\Utility\Unicode;
use Drupal;

/**
 * Returns autocomplete responses for countries.
 */
class CountryAutocompleteController {

  /**
   * Returns response for the country name autocompletion.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The current request object containing the search string.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   A JSON response containing the autocomplete suggestions for countries.
   */
  public function autocomplete(Request $request) {
    $matches = array();
    $string = $request->query->get('q');
    if ($string) {
      $countries = \Drupal::service('country_manager')->getList();
      foreach ($countries as $iso2 => $country) {
        if (strpos(Unicode::strtolower($country), Unicode::strtolower($string)) !== FALSE) {
          $matches[] = array('value' => $country, 'label' => $country);
        }
      }
    }
    return new JsonResponse($matches);
  }
}

Whatever we type in our autocomplete widget will get passed to our autocomplete method. Then, we simply search for it in the array of country names which we pull from the country_manager service we have come across before. Finally, we return any matches or an empty array in a JSON response.

That looks more like it now and we’re nearly there. If you look back at our formElement() method in CountryAutocompleteWidget.php we specified a validation callback. We are going to do that now in our country.module file. Add this code:

/**
 * @file
 * Defines simple country field type.
 */

use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Form\FormStateInterface;

/**
 * Form element validate handler for country autocomplete element.
 */
function country_autocomplete_validate($element, FormStateInterface $form_state) {
  if ($country = $element['#value']) {
    $countries = \Drupal::service('country_manager')->getList();
    $iso2 = array_search($country, $countries);
    if (!empty($iso2)) {
      $form_state->setValueForElement($element, $iso2);
    }
  }
}

We get our array of countries, and compare the value we want to send to the database with possible values. Now that we’re here, let’s just implement hook_help to give some information about our module. Just add this below the last use statement:

/**
 * Implements hook_help().
 */
function country_help($route_name, RouteMatchInterface $route_match) {
  switch ($route_name) {
    case 'help.page.country':
      $output = '';
      $output .= '<h3>' . t('Country') . '</h3>';
      $output .= '<p>' . t('The Country module defines a simple country field type for the Field module. It provides 2 widgets - select options and autocomplete textfield - for this purpose.  See the <a href="https://capgemini.github.io/drupal/writing-custom-fields-in-drupal-8//!field-help">Field module help page</a> for more information about fields.', array('!field-help' => url('admin/help/field_ui'))) . '</p>';
      return $output;
  }
}

We have now finished our autocomplete widget and learned something about routing. Not bad at all!

Data Output

We have everything ready for creating our field and allowing users to input data. Everything should work. There’s little work left before we can display the output. That’s where the need for a field formatter comes in. Add a new folder: src\Plugin\Field\FieldFormatter and inside it create CountryDefaultFormatter.php. Then add this code.

/**
 * @file
 * Definition of Drupal\country\Plugin\field\formatter\CountryDefaultFormatter.
 */

namespace Drupal\country\Plugin\Field\FieldFormatter;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
use Drupal;

/**
 * Plugin implementation of the 'country' formatter.
 *
 * @FieldFormatter(
 *   id = "country_default",
 *   module = "country",
 *   label = @Translation("Country"),
 *   field_types = {
 *     "country"
 *   }
 * )
 */
class CountryDefaultFormatter extends FormatterBase {
}

If we don’t do anything now, everything will work except where we expect to see a country name, we will see the ISO-2 which was saved as the value of the field. To display our country name, we need to override the viewElements() method. So let’s do it:

/**
 * {@inheritdoc}
 */
public function viewElements(FieldItemListInterface $items) {
    $elements = array();
    $countries = \Drupal::service('country_manager')->getList();
    foreach ($items as $delta => $item) {
      if (isset($countries[$item->value])) {
        $elements[$delta] = array('#markup' => $countries[$item->value]);
      }
    }
    return $elements;
}

Once again, we get our array of countries, find the country name for our ISO-2 value and return it as markup. Job done. The module we have created is now functional and can now be installed.

Field UI

Let’s take a look at what fields UI offer us with our Country field. You may use an existing content type or start off with a new one. From the list of content types (admin/structure/types) clicking on the Edit operation will take you to the edit form with three tabs at the top: Manage fields, Manage form display, and Manage display which give us access to our field type, field widget and field formatter, respectively.

  • Field type (Manage fields):

Click “Add field”. Select “Country” from the “Add a new field” select dropdown. That dropdown is populated by all @FieldType plugins from all enabled modules. They are grouped according to the “category” key of field plugins. This is optional, and if it isn’t defined for a field, it goes under “General”. If you inspect it, you’ll see that the name is “country_default” which matches the “id” key of our plugin and the “Country” we see there is the “label”.

Enter “Location” as the field name in the “Label” textfield and save it.

If you want to see how the field type settings are reflected in the field UI, go back to the annotation in CountryItem.php. Edit your plugin label, change it to something like “Country - edited” and save it. Then go back to the fields UI page and you’ll see not only the new value in the select dropdown when creating a new field but also under the “Field Type” column for the new Location field. You can revert the change now.

  • Field widget (Manage form display):

On this screen you can see what was defined in the FieldWidget plugin. In the URL you can see the plugin “id” and the “label” is the description.

  • Field formatter (Manage display):

There isn’t much choice here so we just leave the default formatter as is.

You may now take the completed field module for a spin. Finish by adding it to a content type, create a node and view the output. That’s all. We’ve come to the end of looking at how to create custom field modules and in the process learned a few things about Drupal 8. Go make yourself a coffee. You deserve it!

1: See https://www.drupal.org/node/1882526 for an excellent introduction to annotation-based plugins.

May 14 2015
May 14

As Drupal has evolved, it has become more than just a CMS. It is now a fully fledged Web Development Platform, enabling not just sophisticated content management and digital marketing capabilities but also any number of use cases involving data modelling and integration with an endless variety of applications and services. In fact, if you need to build something which responds to an HTTP request, then you can pretty much find a way to do it in Drupal.

“Just because you can, doesn’t mean you should.”

However, the old adage is true. Just because you can use use a sledgehammer to crack a nut, that doesn’t mean you’re going to get the optimal nut-consumption-experience at the end of it.

Drupal’s flexibility can lead to a number of different integration approaches, all of which will “work”, but some will give better experiences than others.

On the well trodden development path of Drupal 8, giant steps have been taken in making the best of what is outside of the Drupal community and “getting off the island”, and exciting things are happening in making Drupal less of a sledgehammer, and more of a finely tuned nutcracker capable of cracking a variety of different nuts with ease.

In this post, I want to explore ways in which Drupal can create complex systems, and some general patterns for doing so. You’ll see a general progression in line with that of the Drupal community in general. We’ll go from doing everything in Drupal, to making the most of external services. No option is more “right” than others, but considering all the options can help make sure you pick the approach that is right for you and your use case.

Build it in Drupal

One option, and probably the first that occurs to many developers, is to implement business logic, data structures and administration of a new applications or services using Drupal and its APIs. After all, Entity API and the schema system give us the ability to model custom objects and store them in the Drupal database; Views gives us the means to retrieve that data and display it in a myriad of ways. Modules like Rules; Features and CTools provide extensive options for implementing specific business rules to model your domain specific data and application needs.

This is all well and good, and uses the strengths of Drupal core and the wide range of community contributed modules to enable the construction of complex sites with limited amounts of coding required, and little need to look outside Drupal. The downside can come when you need to scale the solution. Depending on how the functionality has been implemented you could run into performance problems caused by large numbers of modules, sub-optimal queries, or simply the amount of traffic heading to your database - which despite caching strategies, tuning and clustering is always likely to end up being the performance bottleneck of your Drupal site.

It also means your implementation is tightly coupled to Drupal - and worse, most probably the specific version of Drupal you’ve implemented. With Drupal 8 imminent this means you’re most likely increasing the amount of re-work required when you come to upgrade or migrate between versions.

It’s all PHP

Drupal sites can benefit hugely from being part of the larger PHP ecosystem. With Drush make, the Libraries API, Composer Manager, and others providing the means of pulling external, non-Drupal PHP libraries into a Drupal site, there are huge opportunities for building complexity in your Drupal solution without tying yourself to specific Drupal versions, or even to Drupal at all. This could become particularly valuable as we enter the transition period between Drupal 7 and 8.

In this scenario, custom business logic can be provided in a framework agnostic PHP library and a Naked Module approach can be used to provide the glue between that library and Drupal - utilising Composer to download and install dependencies.

This approach is becoming more and more widespread in the Drupal community with Commerce Guys (among others) taking a libraries first approach to many components of Commerce 2.x which will have generic application outside of Drupal Commerce.

The major advantage of building framework agnostic libraries is that if you ever come to re-implement something in another framework, or a new version of Drupal, the effort of migrating should be much lower.

Integrate

Building on the previous two patterns, one of Drupal’s great strengths is how easy it is to integrate with other platforms and technologies. This gives us great opportunity to implement functionality in the most appropriate technology and then simply connect to it via web services or other means.

This can be particularly useful when integrating with “internal” services - services that you don’t intend to expose to the general public (but may still be external in the sense of being SaaS platforms or other partners in a multi-supplier ecosystem). It is also a useful way to start using Drupal as a new part of your ecosystem, consuming existing services and presenting them through Drupal to minimise the amount of architectural change taking place at one time.

Building a solution in this componentised and integrated manner gives several advantages:

  • Separation of concerns - the development, deployment and management of the service can be run by a completely separate team working in a different bounded context. It also ensures logic is nicely encapsulated and can be changed without requiring multiple front-end changes.
  • Horizontal scalability - implementing services in alternate technologies lets us pick the most appropriate for scalability and resilience.
  • Reduce complex computation taking place in the web tier and let Drupal focus on delivering top quality web experience to users. For example, rather than having Drupal publish and transform data to an external platform, push the raw data into a queue which can be consumed by “non-Drupal” processes to do the transform and send.
  • Enable re-use of business logic outside of the web tier, on other platforms or with alternative front ends.

Nearly-Headless Drupal

Headless Drupal is a phrase that has gained a lot of momentum in the Drupal community - the basic concept being that Drupal purely responds with RESTful endpoints, and completely independant front-end code using frameworks such as Angular.js is used to render the data and completely separate content from presentation.

Personally, I prefer to think of a “nearly headless” approach - where Drupal is still responsible for the initial instantiation of the page, and a framework like Angular is used to control the dynamic portion of the page. This lets Drupal manage the things it’s good at, like menus, page layout and content management, whilst the “app” part is dropped into the page as another re-usable component and only takes over a part of the page.

For an example use case, you may have business requirements to provide data from a service which is also provided as an API for consumption by external parties or mobile apps. Rather than building this service in Drupal, which while possible may not provide optimal performance and management opportunities, this could be implemented as a standalone service which is called by Drupal as just another consumer of the API.

From an Angular.js (or insert frontend framework of choice) app, you would then talk directly to the API, rendering the responses dynamically on the front end, but still use Drupal to build everything and render the remaining elements of the page.

Summing up

As we’ve seen, Drupal is an incredibly powerful solution, providing the capability for highly-consolidated architectures encapsulated in a single tool, a perfect enabler for projects with low resources and rapid development timescales. It’s also able to take its place as a mature part of an enterprise architecture, with integration capabilities and rich programming APIs able to make it the hub of a Microservices or Service Oriented Architecture.

Each pattern has pros and cons, and what is “right” will vary from project to project. What is certain though, is that Drupal’s true strength is in its ability to play well with others and do so to deliver first class digital experiences.

New features in Drupal 8 will only continue to make this the case, with more tools in core to provide the ability to build rich applications, RESTful APIs for entities out of the box allowing consumption of that data on other platforms (or in a headless front-end), improved HTTP request handling with Guzzle improving options for consuming services outside of Drupal, and much more.

Feb 27 2015
Feb 27

There are thousands of situations in which you do not want to reinvent the wheel. It is a well known principle in Software Engineering, but not always well applied/known into the Drupal world.

Let’s say for example, that you have a url that you want to convert from relative to absolute. It is a typical scenario when you are working with Web (but not just Web) crawlers. Well, you could start building your own library to achieve the functionality you are looking for, packaging all in a Drupal module format. It is an interesting challenge indeed but, unless for training or learning purposes, why wasting your time when someone else has already done it instead of just focussing on the real problem? Especially if your main app purpose is not that secondary problem (the url converter).

What’s more, if you reuse libraries and open source code, you’ll probably find yourself in the situation in which you could need an small improvement in that nice library you are using. Contributing your changes back you are closing the circle of the open source, the reason why the open source is here to stay and conquer the world (diabolical laugh here).

That’s another one of the main reasons why lot’s of projects are moving to the Composer/Symfony binomium, stop working as isolated projects and start working as global projects that can share code and knowledge between many other projects. It’s a pattern followed by Drupal, to name but one, and also by projects like like phpBB, ezPublish, Laravel, Magento,Piwik, …

Composer and friends

Coming back to our crawler and the de-relativizer library that we are going to need, at this point we get to know Composer. Composer is a great tool for using third party libraries and, of course, for contributing back those of your own. In our web crawler example, net_url2 does a the job just beautifully.

Nice, but at this point you must be wondering… What does this have to do with Drupal, if any at all? Well, in fact, as everyone knows, Drupal 8 is being (re)built following this same principle (DRY or don’t repeat yourself) with an strong presence of the great Symfony 2 components in the core. Advantages? Lots of them, as we were pointing out, but that’s the purpose of another discussion

The point here is that you don’t need to wait for Drupal 8, and what’s more, you can start applying some of this principles in your Drupal 7 libraries, making your future transition to Drupal 8 even easier.

Let’s rock and roll

So, using a php library or a Symfony component in Drupal 7 is quite simple. Just:

  1. Install composer manager
  2. Create a composer.json file in your custom module folder
  3. Place the content (which by the way, you’ll find quite familiar if you’ve already worked with Symfony / composer yaml’s):
    "require": {
      "pear/net_url2": "2.0.x-dev"
     }
    
    
  4. enable the custom module

And that’s it basically. At this point we simply need to tell drupal to generate the main composer.json. That’s basically a composer file generated from the composer.json found in each one of the modules that include a composer themselves.

Lets generate that file:

drush composer-rebuild

At this point we have the main composer file, normally in a vendor folder (if will depend on the composer manager settings).

Now, let’s make some composer magic :

drush composer update

At this point, inside the vendors folder we should now have a classmap, containing amongst others our newly included library.

Hopefully all has gone well, and just like magic, the class net_url2 is there to be used in our modules. Something like :

$base = new Net_URL2($absoluteURL);

Just remember to add the library to your class. Something like:

use Net_URL2;

In the next post we’ll be doing some more exciting stuff. We will create some code that will live in a php library, completely decoupled but at the same time fully integrated with Drupal. All using Composer magic to allow the integration.

Why? Again, many reasons like:

  1. Being ready for Drupal 8 (just lift libraries from D7 or D6 to D8),
  2. Decoupling things so we code things that are ready to use not just in Drupal, and
  3. Opening the door to other worlds to colaborate with our Drupal world, …
  4. Why not use Dependency Injection in Drupal (as it already happens in D8)? What about using the Symfony Service container? Or something more light like Pimple?
  5. Choose between many other reasons…

See you in my next article about Drupal, Composer and friends, on the meantime, be good :-).

Updated: Clarified that we are talking about PHP Libraries and / or Symfony components instead of bundles. Thanks to @drrotmos and @Ross for your comments.

Jan 14 2015
Jan 14

Up until Drupal 8 there has been little to encourage well organised code. It now has PSR-4 autoloading so your classes are automatically included. Even though Drupal 8 is just round the corner, a lot of us will still be using Drupal 7 for quite a while, however that doesn’t mean we can’t benefit from this structure in Drupal 7.

This post covers two parts:

  1. Autoloading class files.
  2. Avoiding extra plumbing to hook into your class methods.

You’re probably familiar with drupal_get_form(‘my_example_form’) which then looks for a function my_example_form(). The issue is that your form definition will no longer be in such a function but within a method in a class. To cover both these parts we will be using two modules:

  1. XAutoLoad - Which will autoload our class.
  2. Cool - Which allows us to abstract the usual functions into class methods.

Drupal 8 was originally using PSR-0 which has been deprecated in favour of PSR-4. As a consequence the Cool module uses PSR-0 in its examples although it does support PSR-4. We will create an example module called psr4_form.

The information on autoloading and folder structure for PSR-4 in Drupal 8 states that we should place our form class in psr4_form/src/Form/FormExample.php however the cool module instead loads from a FormControllers folder: psr4_form/src/FormControllers/FormExample.php.

We can get round this by providing our own hook_forms() as laid out in the Cool module:

/**
* Implements hook_forms().
*/
function psr4_form_forms($form_id, $args) {
  $classes = \Drupal\cool\Loader::mapImplementationsAvailable('Form', '\Drupal\cool\Controllers\FormController');
  unset($classes['Drupal\\cool\\BaseForm']);
  unset($classes['Drupal\\cool\\BaseSettingsForm']);
  $forms = array();
  foreach ($classes as $class_name) {
    $forms[$class_name::getId()] = array(
      'callback' => 'cool_default_form_callback',
      'callback arguments' => array($class_name),
    );
  }

  return $forms;
}

If you are ok placing your class in the FormControllers folder then you can omit the above function to keep your .module file simple or you could put the hook in another module. Potentially the Cool module could be updated to reflect this.

This class requires a namespace of the form Drupal\<module_name>\Form. It also extends the BaseForm class provided by the Cool module so we don’t need to explicitly create our form functions:

namespace Drupal\psr4_form\Form;

class FormExample extends \Drupal\cool\BaseForm {
  ...
}

Within our FormExample class we need a method getId() to expose the form_id to Drupal:

public static function getId() {
  return 'psr4_form';
}

And of course we need the form builder:

public static function build() {
  $form = parent::build();
  $form['my_textfield'] = array(
     '#type' => 'textfield',
     '#title' => t('My textfield'),
   );

   return $form;
}

All that is left is to define your validate and submit methods following the Drupal 8 form API.

At the time of writing, the Cool module isn’t up to date with Drupal 8 Form API conventions. I started this blog post with the intention of a direct copy and paste of the src folder. Unfortunately the methods don’t quite follow the exact same conventions and they also need to be static:

Drupal 7 Drupal 8 getId getFormId build buildForm validate validateForm submit submitForm

This example module can be found at https://github.com/oliverpolden/psr4_form.

Drupal 8 is just round the corner but a lot of us will still be using Drupal 7 for the foreseeable future. Taking this approach allows us to learn and make use of Drupal 8 conventions as well as making it easier to migrate from Drupal 7. It would be nice to see the Cool module be brought up to date with the current API, perhaps something I will be helping with in the not so distant future.

Modules

Information

Jan 07 2015
Jan 07

Drupal 8 is the latest version of Drupal, a modern, PHP 5.4-boasting, REST-capable, object-oriented powerhouse. The concepts are still the same as the previous versions but the approach is now different. Drupal 8 comes with a modern Object Oriented Programming (OOP) approach to most parts of the system thanks to the use of the Symfony2 framework.

I took part in the Drupalcon in Amsterdam and I enjoyed a number of really interesting talks about Drupal 8, among those ‘Drupal 8: The Crash Course’ realized and presented by Larry Garfield. In this post the idea is to recap few key points of his talk as I think they are important to fully understand the basics of this new Drupal version. In case you are interested you can also watch the full talk.

How do I define a module?

In Drupal 8 to define a module we need only a YAML (.info.yml) file:

/modules/d8_example_module/d8_example_module.info.yml

name: D8 Test Module
description: D8 Test Module
type: module
core: 8.x
package: Custom

In Drupal 8 the .module file is not required anymore, so with only the .info.yml file the module is ready to be enabled.

How do I make a page?

Start creating a controller extending the ControllerBase class and return the output of the page:

/modules/d8_example_module/src/Controller/D8ExampleModuleController.php

namespace Drupal\d8_example_module\Controller;

use Drupal\Core\Controller\ControllerBase;

class D8ExampleModuleController extends ControllerBase {

  public function test_page($from, $to) {
    $message = $this->t('%from to %to', [
      '%from' => $from,
      '%to' => $to,
    ]);

    return ['#markup' => $message];
  }
}

Once this is done, within the .routing.yml file we can define the path, the controller, the title and the permissions:

/modules/d8_example_module/d8_example_module.routing.yml

d8_example_module.test_page:
  path: '/test-page/{from}/{to}'
  defaults:
    _controller: 'Drupal\d8_example_module\Controller\D8ExampleModuleController::test_page'
    _title: 'Test Page!'
  requirements:
    _permission: 'access content'

How do I make content themeable?

We still have the hook_theme() function to define our theme:

/modules/d8_example_module/d8_example_module.module

/**
 * Implements hook_theme().
 */
function d8_example_module_theme() {
  $theme['d8_example_module_page_theme'] = [
    'variables' => ['from' => NULL, 'to' => NULL],
    'template' => 'd8-theme-page',
  ];

  return $theme;
}

For the template page Drupal 8 uses Twig, a third-party template language used by many PHP projects. For more info about Twig have a look at Twig in Drupal 8. One of the cool parts of Twig is that we can do string translation directly in the template file:

/modules/d8_example_module/template/d8-theme-page.html.twig

<section>
  {% trans %}
    <strong>{{ from }}</strong> to <em>{{ to }}</em>
  {% endtrans %}
</section>

And then we assign the theme to the page:

/modules/d8_example_module/src/Controller/D8ExampleModuleController.php

namespace Drupal\d8_example_module\Controller;

use Drupal\Core\Controller\ControllerBase;

class D8ExampleModuleController extends ControllerBase {

  public function test_page($from, $to) {
    return [
      '#theme' => 'd8_example_module_page_theme',
      '#from' => $from,
      '#to' => $to,
    ];
  }
}

How do I define a variable?

Drupal 8 has a whole new configuration system that uses human-readable YAML (.yml) text files to store configuration items. For more info have a look at Managing configuration in Drupal 8.

We define variables in config/install/*.settings.yml:

/modules/d8_example_module/config/install/d8_example_module.settings.yml

default_count: 3

The variables will be stored in the database during the installation of the module. We define the schema for the variables in config/schema/*.settings.yml:

/modules/d8_example_module/config/schema/d8_example_module.settings.yml

d8_example_module.settings:
  type: mapping
  label: 'D8 Example Module settings'
  mapping:
    default_count:
      type: integer
      label: 'Default count'

How do I make a form?

To create a form we extend a ConfigFormBase class:

/modules/d8_example_module/src/Form/TestForm.php

namespace Drupal\d8_example_module\Form;

use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

class TestForm extends ConfigFormBase {
  public function getFormId() {
    return 'test_form';
  }

  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('d8_example_module.settings');

    $form['default_count'] = [
      '#type' => 'number',
      '#title' => $this->t('Default count'),
      '#default_value' => $config->get('default_count'),
    ];
    return parent::buildForm($form, $form_state);
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $config = $this->config('d8_example_module.settings');
    $config->set('default_count', $form_state->getValue('default_count'));
    $config->save();
    parent::submitForm($form, $form_state);
  }
}

Then within the .routing.yml file we can define the path, the form, the title and the permissions:

/modules/d8_example_module/d8_example_module.routing.yml

d8_example_module.test_form:
  path: /admin/config/system/test-form
  defaults:
    _form: 'Drupal\d8_example_module\Form\TestForm'
    _title: 'Test Form'
  requirements:
    _permission: 'configure_form'

We use another YAML file (.permissions.yml) to define permissions:

/modules/d8_example_module/d8_example_module.permissions.yml

'configure_form':
  title: 'Access to Test Form'
  description: 'Set the Default Count variable'

We also use another YAML file (.links.menu.yml) to define menu links:

/modules/d8_example_module/d8_example_module.links.menu.yml

d8_example_module.test_form:
  title: 'Test Form'
  description: 'Set the Default Count variable'
  route_name: d8_example_module.test_form
  parent: system.admin_config_system

How do I make a block?

To create a block we extend a ConfigFormBase class:

/modules/d8_example_module/src/Plugin/Block/TestBlock.php

namespace Drupal\d8_example_module\Plugin\Block;

use Drupal\Core\Block\BlockBase;

/**
 * Test Block.
 *
 * @Block(
 *   id = "test_block",
 *   admin_label = @Translation("Test Block"),
 *   category = @Translation("System")
 * )
 */
class TestBlock extends BlockBase {

  public function build() {
    return [
      '#markup' => $this->t('Block content...'),
    ];
  }
}

In this way the block is ready to be configured in the CMS (/admin/structure/block). Here is an example of a more complex block:

namespace Drupal\d8_example_module\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;

/**
 * Test Block.
 *
 * @Block(
 *   id = "test_block",
 *   admin_label = @Translation("Test Block"),
 *   category = @Translation("System")
 * )
 */
class TestBlock extends BlockBase {

  public function defaultConfiguration() {
    return ['enabled' => 1];
  }

  public function blockForm($form, FormStateInterface $form_state) {
    $form['enabled'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Configuration enabled'),
      '#default_value' => $this->configuration['enabled'],
    ];

    return $form;
  }

  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->configuration['enabled'] = (bool)$form_state->getValue('enabled');
  }

  public function build() {
    if ($this->configuration['enabled']) {
      $message = $this->t('Configuration enabled');
    }
    else {
      $message = $this->t('Configuration disabled');
    }
    return [
      '#markup' => $message,
    ];
  }
}

Structure of a module

The structure of a module should look like the example module d8_example_module:

d8_example_module/
 |
 |- config/
   |
   |- install/
     |
     |- d8_example_module.setting.yaml
     |
     |- schema/
       |
       |- d8_example_module.settings.yaml
 |
 |- src/
   |
   |- Controller/
     |
     |- D8ExampleModuleController.php
   |
   |- Form/
     |
     |- TestForm.php
   |
   |- Plugin/
     |
     |- Block/
       |
       |- TestBlock.php
 |
 |- templates/
   |
   |- d8-theme-page.html.twig
 |
 |- d8_example_module.info.yml
 |
 |- d8_example_module.links.menu.yml
 |
 |- d8_example_module.module
 |
 |- d8_example_module.permissions.yml
 |
 |- d8_example_module.routing.yml

Drupal 8 in 2 steps: Extend a base Class or implement an Interface and tell Drupal about it.

Download the example module

Updates:

  • 19th January 2015
    • The code in this post has been updated to reflect changes in Drupal 8 Beta 4. Thanks to Geoff Lawrence for the updates to the example repo.
Oct 31 2014
Oct 31

The recent announcement of Drupal SA-CORE-2014-005 caused a global scramble amongst Drupal site owners, developers and hosting providers to patch their sites and protect themselves from the vulnerability. Unfortunate site owners who weren’t quick enough were left with a site audit to ensure all traces of any successful attacks were removed from their environments.

Firstly, hats off to the Drupal Security Team, Core release co-ordinators and the Drupal community in general for a rapid, considered and clear response to the issue. The early warning that a security release was coming was well stated enough to make it clear to many that it would be a major issue. The normal release time was brought forward to a more suitable timeslot to ensure the maximum audience saw it early, and the post-release guidance, FAQs and blog posts covered all aspects from how to patch, what to do if your site had been hacked, and what to look out for to indicate if you had been affected. A follow up public service announcement PSA-2014-003 was published drawing further attention to the risks to unpatched sites and offering further guidance.

Secondly, excellent work from Sektion Eins, the penetration testing experts who detected the vulnerability. This was a defect that has existed for many years, and many organisations will have run penetration tests that haven’t previously detected it. Having been creative enough to find the vulnerability, it was then reported and managed responsibly, which should be praised.

Now, I don’t want to focus on SA-CORE-2014-005 any further, but instead I want to talk about some ways we can protect our Drupal sites and minimise our exposure to vulnerabilities. Whilst bugs will always happen (whether security or functional), there are steps we can all take to mitigate our exposure and respond quickly to incidents.

Early warning systems

The best cure is prevention, and the next best is to catch it early. There are lots of ways to keep up to date with what is going on in Drupal security:

  • The Drupal Security pages contain the most recent core, contrib and public service announcements for Drupal and include RSS feeds
  • Sign up for the Security Announcements mailing list (login to Drupal.org, go to your user profile page and subscribe to the “Security announcements” on the Edit » My newsletters tab)
  • Follow @drupalsecurity on Twitter

All three of these routes will ensure you’re on the quickest possible route to hearing about new security releases. It then becomes quick and easy to review the latest threats, decide if you’re vulnerable (by reviewing the mitigations - a lot of vulnerabilities depend on elevated roles or other mitigations, and these will be clearly described on each Security Advisory), and how quickly you ought to upgrade your core installation or other modules.

It’s also worth having the Update module enabled in your dev environment to phone home and check if there are updates for any of your modules available - however if like me you only login to some sites occasionally then you’ll want a backup strategy for keeping you informed.

It should then become almost ritual that for each alert you:

  • Determine if your core version is affected
  • Determine if you use the contributed module reported as affected
  • Reviewing any mitigations (vulnerability requiring elevated permissions etc) that may keep you safe from the vulnerability and reduce the urgency of patching/upgrading
  • For any urgent updates, apply as soon as practically possible
  • For any non-urgent updates, apply to normal dev/test/release cycle and deploy in your next release slot

Don’t leave the back door open

The great thing about Drupal is the wide range of contributed modules that you can use to kickstart your development and re-use functionality that other people have already built for you. However, this comes with its own risks - as you’re effectively installing code that you’ve not written and so have no guarantees of how secure it may be.

With this in mind, it’s essential to review the modules you download, rather than just installing them right-away. Just as we check if modules contain any potential performance gotchas, we also look to see if there are any obvious security flaws - use of $_GET or $_POST variables in templates, data directly concatenated into SQL queries, no roles or permissions defined, all gives clues, and we can use Drupal secure coding guidelines, and awareness of the OWASP Top 10, to inform our reviews.

To some degree, these checks can be automated by running things like Coder, which includes some basic security checks and has some good proposals to add more security sniffs. Modules like Security Review can also check for some easy-to-make mistakes which may render your site insecure - and are invaluable, particularly if you’re new to site security. However, automated checks are only as good as the information they’re fed with, so there is no substitute for getting in and reading the code - you were going to look to see how it worked anyway, right?

Remember - if you do spot any security flaws in Drupal Core or Contrib, don’t raise issues in the public issue queues, make sure you follow the process to report a security issue to the security team, so that it can be fixed in private and released before the vulnerability is made public.

Constant vigilance

It’s not just Drupal that can be exposed to vulnerabilities such as these, PHP itself has had its share over the years, and realistically no large body of code is going to go its entire life without some vulnerabilities creeping in. There are also many more people trying to find vulnerabilities to take advantage of than there are people trying to fix them.

This means keeping your entire platform secure becomes a constant activity, so a reliable and repeatable test and release cycle becomes essential - if pulling down the latest updates and deploying them takes a significant amount of time, the odds are that you’ll get lazy and skip an udpate or two. This soon becomes the security guys nightmare as sites, services and platforms become more out of date and more vulnerable to attack.

Watch the watchman

All the attention paid to Core and Contrib security updates is very valuable, but it becomes worthless if you don’t give the same care to the code you and your team write in custom modules. An awareness of how to follow Drupal best practices to write secure code is an essential part of the training and education of any good Drupal developer. Fortunately Drupal provides some excellent abstraction layers to remove some of the thinking on these topics, so make sure you use the database layer correctly, handle text securely, and create forms in a safe way, as well as all the other best practice.

It’s also well worth commissioning penetration tests from external specialists to test your implementation and validate that it is secure. We use the expertise of these companies regularly, and rotate through a list of preferred companies to ensure that we’re constantly getting fresh eyes on the solutions, and varied approaches to testing, so as to maximise the chances of finding issues. The tests often prove their worth, and whilst every developer hates having defects raised, we’d all much rather the security issue be in a test environment than in production!

Reading list

To finish, here are some excellent resources to keep up to date with best practice approaches to keeping your Drupal installation secure:

Oct 22 2014
Oct 22

Along with Malcolm and other colleagues from the Capgemini Drupal team, I attended the recent Drupalcon in Amsterdam. And as well as admiring the Dutch attitude to cycling and its integration in the city (btw London, blue paint on the road != a cycle superhighway), we also caught up on the state of Drupal and its future. So here a few reflections from Drupalcon Amsterdam.

Drupal In The Enterprise - A Key Component In The Wider Web

I’ve been to a few Drupalcons now, and compared to previous years, use of Drupal in the enterprise (or more generally at scale) seems much more commonplace. Dries Buytaert’s (Drupal founder) keynotes have made reference to Drupal’s ability to integrate with other systems as a key strength, and in these types of projects, Drupal is not used as the all-in-one solution that maybe was more commonplace a few years ago.

Partly this is also due to the way the web has moved far beyond the idea of ‘a thing you use on your desktop computer’, and Drupal has shown itself to be adaptable to this. For example, the idea of Headless Drupal was a well covered topic this year. Of course, previous ‘cons have had talks on uses of Drupal with other technologies (e.g. node.js talk from London 2011) but whereas it seemed more an interesting edge case then, now there are many successful real-world projects adopting these ideas.

The Sessions

Based on my not-entirely-comprehensive memory of the subset of sessions I attended from past Drupalcons, this year there seemed to be many more talks which could have easily been at a frontend or PHP specific conference. Drupal 8’s use of Symfony 2 components and shift to making use of components Proudly Found Elsewhere is part of this.

A few talks that those of us who attended would recommend (not an exhaustive list). I won’t go into too much detail (that’s all in the slides and the video) but these are worth checking out.

Automated Frontend Testing

The types of frontend testing which can be automated, covering performance (Phantomas), CSS (Wraith) and end-to-end (CasperJS) and integrating this into your build workflow. Slides Video

Models & Service Layers; Hemoglobin & Hobgoblins

I think the PHP track is a welcome addition to Drupalcon. When developing custom functionality on projects here at Capgemini, we often write the business models and logic as separate classes to Drupal which are then ‘glued’ via hooks which implement those classes. That kind of separation has advantages with portability, testability and some amount of simplification in that Drupal isn’t a dependency. Video

Cory Doctorow’s Keynote

Very interesting talk on how open-source is (in some ways) critical to our individual freedom in the modern world. In an age where “a modern house is a computer that you co-inhabit”, if a system went down - or arguably worse, were controlled by overzealous authorities - it can become uninhabitable. What do we do in this case? Is the Apple iTunes/U2 debacle merely the thin end of the wedge? Interesting viewing for anyone who contributes or uses open source. Video

Drupal 8 And The Future

As Drupal 8 entered beta during the conference, it was an opportunity to check out the changes. The plugin system for extending functionality looks interesting. In Drupal projects at Capgemini we have adopted approaches such as abstracting business logic and objects into standalone libraries and classes, called from hooks and callbacks where we need integration with Drupal. This approach allows us easier unit-testing and portability of classes. D8’s plugin system looks like a good way of achieving those advantages while implementing a Drupal API.

Having spent a lot of time on projects wrestling with the various methods of deploying and updating configuration, the CMI (Configuration Management Initiative), which imports and exports YAML files to update and dump site configuration is a very welcome addition.

In the frontend, I’m looking forward to using the Twig templating. The idea of having cleaner PHP-free templates yet still with the flexibility to have filters and basic logic is going to help improve separation between the theme and module layer. It’ll be new to me (as will other things) but as with other components, they have been successfully used in other PHP projects so there is documentation and examples already out there. There are some smaller changes too - removing drupal.js’s dependency on jQuery (thereby gently encouraging use of native JS), updating the versions of included libraries (and committing to keeping them up-to-date during D8’s lifetime) and including no JavaScript by default are good steps to optimising the frontend.

Where things may be more challenging is the APIs which have both new object-oriented components and retain the hook and callback system in some combination (for example, declaring widgets via hook_element_info). To take an example from a core module, the file module’s managed_file widget functionality is spread across a number of callbacks as well as its own FileWidget class. It’s not the most straightforward development flow to follow. Where this has some advantages is that existing modules will not need a complete OO rewrite just to be compatible with D8 - a module author could do a simple port at first before rewriting to take advantage of the new APIs. But some care is need to ensure that the advantages of encapsulation, increased unit-testability and extendability that the OO patterns introduce are not compromised by dependencies on a particular hook or callback.

Taking The Leap

Finally, as Drupal 8 progressed from alpha to beta during Amsterdam Drupalcon, it does seem now that it can be realistically considered for projects coming up on the horizon. Obviously there will a lot more work going into the project to fix bugs and improve performance and so forth. But now the major API decisions and changes have been made. But with this iteration of Drupal incorporating many more features from the contrib world (Views, WYSIWYG, etc) and PHP (Symfony2 components), it looks to be a healthy position for use when that 8.0.0 finally lands.

Oct 20 2014
Oct 20

Recently 10 members of the Drupal development team at Capgemini went to Drupalcon Amsterdam. Having been to two Drupalcons before, I more or less knew what to expect, but something I hadn’t previously given much thought to was how big an event it is. Compared to most other web conferences, it’s a beast. For me personally, I wonder if it’s getting too big and too impersonal, and I think that I’ll be more interested in going to smaller events.

Some of the more interesting sessions for me were the BoFs - in particular a discussion of open source training material and apprenticeships provided a lot of food for thought, and hopefully we can get involved at some level. Capgemini already does a lot of work getting graduates and apprentices into our engineering practice, and with such a big Drupal team, I hope we can benefit from and contribute to the Open Drupal initiative in 2015.

Whenever I go to an event, I come back with a to-do list, and this was no exception. I’ll definitely be digging further into CasperJS following Chris Ruppel’s session on Automated Frontend Testing. I was also very interested to hear about the way that Lullabot spin up test environments for pull requests - it will be good to investigate the feasibility of incorporating this into our workflow.

The other talk that stood out for me was John Albin Wilkins on Styleguide-Driven Development. For a long time, I’ve had a bee in my bonnet about the value of component libraries over Photoshop comps, and it was good to be reminded that I’m not alone. In an interesting session, John outlined an approach to integrating component-based design and automated style guides to agile development projects.

It’s been said many times before, but it’s worth remembering that all too often, people are still thinking in terms of pages, rather than systems.

In the context of the work that we do, this is even more important. We’re a large development team, building CMS-driven sites for large corporate clients, where the design is done by a team working for another company. We’ve made some inroads into building a more collaborative process, but it’s still too easy to end up with the designers throwing things over the wall to the developers. Very often the designer isn’t closely involved during the build phase, and design tweaks are agreed between the client and the developer without the opportunity to go back to get the designer’s opinion.

This is the whole point of living style guides - component libraries that stay in sync with the code as it evolves. As Shay Howe has discussed, component libraries help everyone on the project.

Designers are reminded of the visual language of the project, and it’s easier for them to see when they’re about to reinvent the wheel.

Style guides help developers by defining and documenting standards, and make it easier to dig in and find the way you solved some problem six months ago.

The projects we work on are large and complex, with a long lifecycle, and as developers we need to value maintainability of the front end code. Part of John’s approach to this was his class-naming convention. Having seen Jonathan Snook present on SMACSS I’d thought it was interesting, but to a certain extent it felt like a fancy name for something that was basically common sense. John’s presentation brought the concept to life well, and persuaded me that there’s more to it than that, with an impressive display of flower power.

The other interesting aspect was splitting up SASS files into components, and using KSS to create the style guide - this is something I definitely intend to do on my next project.

Modularity makes sense - it’s how the back-end is going, it’s how Javascript is going, so why shouldn’t design and CSS go the same way?

UPDATED 3rd December 2014: Unfortunately we got Chris Ruppel’s name wrong in the original version of this post, calling him “Dave Rupl”. Sorry Chris.

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