Upgrade Your Drupal Skills

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

See Advanced Courses NAH, I know Enough
Sep 28 2017
Sep 28
September 28th, 2017

If your site was built with Drupal within the last few years, you may be wondering what all the D8 fuss is about. How is Drupal 8 better than Drupal 6 or 7? Is it worth the investment to migrate? What do you need to know to make a decision? In this post we’ll share the top five reasons our customers—people like you—are taking the plunge. If you know you’re ready, tell us.

  1. Drupal 8 has a built-in services-based, API architecture. That means you can build new apps to deliver experiences across lots of devices quickly and your content only needs to live in one place. D8’s architecture means you don’t have to structure your data differently for each solution—we’ve helped clients build apps for mobile, Roku, and Amazon Alexa using this approach (read how we helped NBC). If you’re on Drupal 6 now, a migration to Drupal 8 will allow you to do unleash the power of your content with API integration.
  2. You can skip Drupal 7 and migrate straight to D8. If you’re on Drupal 6, migrating directly to Drupal 8 is not just doable—it’s advisable. It will ensure every core and contributed module, security patch, and improvement is supported and compatible for your site for longer.
  3. The Drupal 8 ecosystem is ready. One of the reasons people love Drupal is for the amazing variety of modules available. Drupal 8 is mature enough now that most of the major Drupal modules you have already work for D8 sites.
  4. Drupal 8 is efficient. Custom development on Drupal 8 is more efficient than previous versions—we’ve already seen this with our D8 clients and others in the Drupal community are saying the same thing. When you add that to the fact that Drupal 8 is the final version to require migration—all future versions will be minor upgrades—you’ve got a solid business reason to move to Drupal 8 now.
  5. It’s a smart business decision. Drupal 6 is no longer supported—and eventually Drupal 7 will reach “end of life”—which means any improvements or bug fixes you’re making to your existing site will need to be re-done when you do make the move. Migrating to Drupal 8 now will ensure that any investments you make to improving or extending your digital presence are investments that last.

If you’re still not sure what you need, or if you would like to discuss a custom review and recommendation, get in touch. At Four Kitchens, we provide a range of services, including user experience research and design, full-stack development, and support services, each with a strategy tailored to your success.

LET’S TALK!

Read about American Craft Council’s move to Drupal 8
Your site should use component-based theming, here’s how
See what we’ve done for other clients >>
Read more about the services we provide >>
Meet the team >>

Web Chef Todd Ross Nienkerk
Todd Ross Nienkerk

Todd Ross Nienkerk is the CEO and co-founder of Four Kitchens. He was born in a subterranean cave in the future.

Apr 24 2017
Apr 24
April 24th, 2017

Making Huge Strides Back to Desktop

So what is this Electron thing everyone keeps talking about? Even if you haven’t heard of it, you may have used it! With over 4 millions daily users on Slack’s business oriented chat system, their cross-platform desktop application helps them reach their users outside of browsers, but these systems are in fact part of the same thing.

Back in May 2014, prolific bastions of open source and $2b valuated company, GitHub, took the custom application wrapper it originally created for its Atom code editor and released into the world—and Electron was born. Rebranded from “Atom Shell” in 2015, Electron began to take off almost immediately, allowing regular web developers the ability to make native-like, high performance desktop applications using the exact same HTML, CSS, and JavaScript technologies they use to make the rest of the web.

Piggybacking on the huge wave of API first work in Drupal 8 utilized via the Waterwheel client wrapper, building with Electron allows you to create nearly native desktop experiences using frameworks like React, Redux, Angular, or anything else that your team can construct to run in a web browser. Beyond even that, Electron gives JavaScript direct access to low level Node.js and operating system APIs, allowing your application direct file access, running custom binaries for data processing, execution of alternative scripting languages, serial port or hardware access, and tons more.

Supercharge Your Next Web App

This year at DrupalCon Baltimore, we present “Supercharge Your Next Web App with Electron”, a session that digs deep and covers everything you need in order to dip into the waters of Electron. We’ll talk about what big companies have already taken the plunge and even provide a checklist for when not to move from the web to a desktop app.

Though an Electron app may not be the right choice for your next application, knowing what tools are available to you—and understanding their incredible possibilities—is going to serve you anytime you’re  considering user-oriented frameworks. Don’t miss out on this interesting view into a future of low-energy/high-return desktop applications in the DrupalCon Horizons track this year.

And, during active exposition hours, make sure to come over to the Four Kitchens booth to see a live demo of an Electron app powered by JavaScript—we build a robot artist!

Four Kitchens: We make content go

Recommended Posts

  • In this issue: Launching the new EW.com, MeteorJS; plus Sane Stack, Herp Derpsum, and switching to Sublime Text 3.
  • Fun & Games DrupalCon Baltimore is next week and we’re so excited to get back together in Baltimore! As the official Drupal Games sponsors, we take fun very seriously and…
  • "API First" or, as some may call it, "Decoupled Drupal", remains a topic of much discussion among the Drupal community. Here are just a few sessions being presented at Drupalcon…
James Todd
James Todd

James tinkers with hardware, software, and everything in between.

Events

Blog posts about ephemeral news, events, parties, conferences, talks—anything with a date attached to it.

Read more Events

Drupal 8 Queue API – Powerful Manual and Cron Queueing

Dec 11 2015
Dec 11

Custom Display Suite Fields in Drupal 8

Oct 14 2015
Oct 14
Oct 20 2014
Oct 20

How to Build a Drupal 8 Module

Please be aware that due to the development process Drupal 8 has been undergoing at the time of writing, some parts of the code might be outdated. Take a look at this repository in which I try to update the example code and make it work with the latest Drupal 8 release.

With the incorporation of many Symfony components into Drupal in its 8th version, we are seeing a shift away from many Drupalisms towards more modern PHP architectural decisions. For example, the both loved and hated hook system is getting slowly replaced. Plugins and annotations are taking away much of the need for info hooks and the Symfony Event Dispatcher component is replacing some of the invoked hooks. Although they remain strong in Drupal 8, it’s very possible that with Drupal 9 (or maybe 10) hooks will be completely removed.

In this article we are going to primarily look at how the Symfony Event Dispatcher component works in Drupal. Additionally, we will see also how to invoke and then implement a hook in Drupal 8 to achieve similar goals as with the former.

To follow along or to get quickly started, you can find all the code we work with here in this repository. You can just install the module and you are good to go. The version of Drupal 8 used is the first BETA release so it’s preferable to use that one to ensure compatibility. Alpha 15 should also work just fine. Let’s dive in.

What is the Event Dispatcher component?

A very good definition of the Event Dispatcher component can be found on the Symfony website:

The EventDispatcher component provides tools that allow your application components to communicate with each other by dispatching events and listening to them.

I recommend reading up on that documentation to better understand the principles behind the event dispatcher. You will get a good introduction to how it works in Symfony so we will not cover that here. Rather, we will see an example of how you can use it in Drupal 8.

Drupal 8 and the Event Dispatcher

For the better part of this article, we will focus on demonstrating the use of the Event Dispatcher in Drupal 8. To this end, we will create a simple demo module (event_dispatcher_demo) that has a configuration form which saves two values as configuration. Upon saving this form, we will dispatch an event that contains the config object and which will allow other parts of the application to intercept and modify it before being saved. Finally, we will do just that by demonstrating how to subscribe (or listen) to these events.

In Drupal 7, this type of modularity is only achieved with hooks. Hooks are being invoked and modules have the option to implement them and contribute with their own data. At the end of this article, we will see how to do that as well in Drupal 8. But first, let’s get on with our demo module.

If you don’t know the basics of Drupal 8 module development, I recommend checking out my previous articles in this series.

The form

The first thing we need is a simple config form with two fields. In a file called DemoForm.php located in the src/Form folder, we have the following:

<?php

/**
 * @file
 * Contains Drupal\event_dispatcher_demo\Form\DemoForm.
 */

namespace Drupal\event_dispatcher_demo\Form;

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

class DemoForm extends ConfigFormBase {

  /**
   * {@inheritdoc}
   */
  public function getFormID() {
    return 'demo_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('event_dispatcher_demo.demo_form_config');
    $form['my_name'] = [
      '#type' => 'textfield',
      '#title' => $this->t('My name'),
      '#default_value' => $config->get('my_name'),
    ];
    $form['my_website'] = [
      '#type' => 'textfield',
      '#title' => $this->t('My website'),
      '#default_value' => $config->get('my_website'),
    ];
    return parent::buildForm($form, $form_state);
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {

    parent::submitForm($form, $form_state);

    $config = $this->config('event_dispatcher_demo.demo_form_config');

    $config->set('my_name', $form_state->getValue('my_name'))
      ->set('my_website', $form_state->getValue('my_website'));
      
    $config->save();
  }

}

Let’s also create a route for it (in the event_dispatcher_demo.routing.yml file) so we can access the form in the browser:

event_dispatcher_demo.demo_form:
  path: 'demo-form'
  defaults:
    _form: '\Drupal\event_dispatcher_demo\Form\DemoForm'
    _title: 'Demo form'
  requirements:
    _permission: 'access administration pages'

So now if you point your browser to example.com/demo-form, you should see the form. Submitting it will create and persist a configuration object called event_dispatcher_demo.demo_form_config that contains two fields: my_name and my_website .

The event dispatcher

Now it’s time to work on the form submit handler (the formSubmit() method) and dispatch an event when the form is saved. This is what the new method will look like:

public function submitForm(array &$form, FormStateInterface $form_state) {

  parent::submitForm($form, $form_state);

  $config = $this->config('event_dispatcher_demo.demo_form_config');

  $config->set('my_name', $form_state->getValue('my_name'))
    ->set('my_website', $form_state->getValue('my_website'));

  $dispatcher = \Drupal::service('event_dispatcher');

  $e = new DemoEvent($config);

  $event = $dispatcher->dispatch('demo_form.save', $e);

  $newData = $event->getConfig()->get();

  $config->merge($newData);

  $config->save();
}

So what happens here? After we take the submitted values and add them to the config object like before, we retrieve the event dispatcher object from the service container:

$dispatcher = \Drupal::service('event_dispatcher');

Please keep in mind that it’s recommended you inject this service into your class, but for brevity, we will retrieve it statically. You can read this article about dependency injection and the service container for more information.

Then we create a new DemoEvent object and pass it the $config through its constructor (we have not yet created the DemoEvent class, we will do that in a minute). Next, we use the dispatcher to dispatch an event of our type and assign this action the identifier demo_form.save. This will be used when subscribing to events (we’ll see this later). The dispatch() method returns the event object with modifications made to it so we can retrieve the config values that may or may not have been altered elsewhere and merge them into our original configuration. Finally, we save this object like we did initially.

Before moving onto the event subscription part of our application, let’s create the DemoEvent class we just instantiated above. In a file called DemoEvent.php located in the src/ folder of our module, we have the following:

<?php

/**
 * @file
 * Contains Drupal\event_dispatcher_demo\DemoEvent.
 */

namespace Drupal\event_dispatcher_demo;

use Symfony\Component\EventDispatcher\Event;
use Drupal\Core\Config\Config;

class DemoEvent extends Event {

  protected $config;

  /**
   * Constructor.
   *
   * @param Config $config
   */
  public function __construct(Config $config) {
    $this->config = $config;
  }

  /**
   * Getter for the config object.
   *
   * @return Config
   */
  public function getConfig() {
    return $this->config;
  }

  /**
   * Setter for the config object.
   *
   * @param $config
   */
  public function setConfig($config) {
    $this->config = $config;
  }

} 

As you can see, this is a simple class that extends the default Event class and which defines setters and getters for the config object we will be passing around using this event. And since we created it, let’s also make sure we use it in the file where we defined the form:

use Drupal\event_dispatcher_demo\DemoEvent;

The event subscriber

Now that our form is functioning normally and an event is being dispatched when the form is saved, we should take advantage of that and subscribe to it. Let’s start with the event subscriber class that implements the EventSubscriberInterface. Inside a file called ConfigSubscriber.php (name of your choice) located in the src/EventSubscriber/ folder, we have the following:

<?php

/**
 * @file
 * Contains Drupal\event_dispatcher_demo\EventSubscriber\ConfigSubscriber.
 */

namespace Drupal\event_dispatcher_demo\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class ConfigSubscriber implements EventSubscriberInterface {

  static function getSubscribedEvents() {
    $events['demo_form.save'][] = array('onConfigSave', 0);
    return $events;
  }

  public function onConfigSave($event) {

    $config = $event->getConfig();

    $name_website = $config->get('my_name') . " / " . $config->get('my_website');
    $config->set('my_name_website', $name_website);
  }

}

So what happens here? The EventSubscriberInterface has only one required method called getSubscribedEvents(). This method is used to register events and callbacks to these events. So above we registered the callable onConfigSave() (found in the same class below) to the event dispatched with the identifier of demo_form.save. And in the callback method we simply add another value to the config object (based on a concatenation of the existing two values). The latter part is just for our demo purposes: here you can do what you want.

When we subscribed our onConfigSave() method to listen to the demo_form.save event, we passed a weight of 0. If you register multiple callbacks to the same event, this weight becomes important (the higher the number, the earlier it gets called). And if a callback alters the same values as one triggered before, they will get overridden. It’s good to keep this in mind.

Now in order for this event subscriber to work, we need to define it as a service and give it the event_subscriber tag. So in a file called event_dispatcher_demo.services.yml found in the root folder of our module, we will have this:

services:
  event_dispatcher_demo.config_subscriber:
    class: Drupal\event_dispatcher_demo\EventSubscriber\ConfigSubscriber
    tags:
      - { name: event_subscriber }

This is a simple service definition with the right tag that will make the container automatically instantiate an object of this class whenever the dispatcher is in play. And that is pretty much it. Clear the cache and if you now save the form again, the configuration object that gets saved will always contain a new value that is based on the first two.

Hooks

In the final part of this article we will demonstrate the use of hooks to achieve a similar goal.

First, let’s change the form submit handler and instead of dispatching events, we will invoke a hook and pass the config values to it. This is what the new submitForm() method will look like:

public function submitForm(array &$form, FormStateInterface $form_state) {

        parent::submitForm($form, $form_state);
        
        $config = $this->config('event_dispatcher_demo.demo_form_config');
        
        $config->set('my_name', $form_state->getValue('my_name'))
          ->set('my_website', $form_state->getValue('my_website'));
        
        $configData = $config->get();
        $newData = \Drupal::service('module_handler')->invokeAll('demo_config_save', array($configData));
        
        $config->merge($newData);
        
        $config->save();
}

We are not using any event objects nor the dispatcher service. Instead, we retrieve the Module Handler service that contains the invokeAll() method used to invoke hook implementations from all modules. This is essentially replacing the Drupal 7 module_invoke_all() helper. And again, it is recommended to inject this service, but for brevity, we’ll retrieve it statically.

The hook implementation invoked in our case is hook_demo_config_save and it gets one parameter, an array of values pulled from our config object. Inside $newData we will have an array of values merged from all the implementations of this hook. We then merge that into our config object and finally save it.

Let’s quickly see an example hook implementation. As with Drupal 7, these can only be in .module files:

/**
 * Implements hook_demo_config_save().
 */
function event_dispatcher_demo_demo_config_save($configValues) {

  $configValues['my_name_website'] = $configValues['my_name'] . " / " . $configValues['my_website'];

  return $configValues;

}

As you can see, we are adding a new value to the config array that will later be merged into the object getting persisted. And we have essentially the same thing as we did with the event dispatcher.

Conclusion

In this article we have taken a look at how the Symfony Event Dispatcher component works in Drupal 8. We’ve learned how flexible it makes our application when it comes to allowing others to extend functionality. Additionally, we’ve seen how the invoked hooks work in the new version of Drupal. Not much has changed since Drupal 7 in this respect apart from the frequency with which they are used. Many hooks have been replaced by plugins and annotations and the Event Dispatcher component has also taken on a big chunk of what was in D7 a hook responsibility.

Although the Event Dispatcher approach is more verbose, it is the recommended way to go forward. Where possible, we no longer use the old procedural approach characteristic to hooks but rather object oriented, decoupled and testable solutions. And Symfony helps greatly with that.

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