Hello, my name is Matthias Vandermaesen. I build websites with Drupal and Wordpress. I live in Belgium and work as a developer for XIO. I love writing, coding, climbing, listening to music and cooking.

Colada is my little digital factory.

Not so long ago, the toolbelt of any web developer was limited. Building for the web wasn't a very complicated affair. With a good understanding of PHP, HTML, CSS and Javascript, one could tackle most problems. The web wasn't a hard place to understand as the concept of a "website" entailed a collection of interlinked hypertext documents. Of course, there were the obvious frustrations with browsers and standards and most developers had there own idiosyncracies when it came to writing code. But the knowledge of a single developer encompassed all he needed to build an entire production ready website.

Today, things are different.

Over the span of a few years, the Web has exploded into this vast array of technologies, applications, frameworks, languages,... expanding and warping the very definition of what the Web actually *is*. No longer is the display of information limited to the confines of a static HTML pages within a browser. No longer are we passive consumers of information on the web. New, mobile, devices and applications have pushed the Web to a new level where we are constantly and actively engaged. This rapid evolution brings new requirements which pushes technology to break out of existing constraints. Technology needs to be geared towards superfluous consuming of information.

As a web developer, such change might feel as an uphill battle. Building a web application isn't simply whipping up some PHP, HTML, CSS, JS, MySQL. There's a vast choice in frameworks and languages out there to help you do the job. And the hard truth is: you just can't master them all. With the advent of the new web, the increasing complexity leads inevitably to specialisation. A developer is proficient in a few frameworks and knows his way around others enough to pick up the basics and get going. Development also became a team based effort where different roles take on different parts of the work.

And then there's the worry of overspecialisation. I can tell you what makes Drupal and WordPress tick and how to bend them. But I don't want my focus to become limited to a few CMS/F'es let alone to a few aspects or subsystems of those. My major motivation to make a career for myself on the Web is to chase innovation and learn new cool stuff. But there are two major problems with such a pursuit:

  • Time is not on my side. Life shifts one's priorities. Adult life comes with its' own priorities. I'm no longer a student with the leisure of time to just hack on stuff. After a hard day of Drupal work, there's only so much time left to read up on new things.
  • The Web has picked up the pace and spawned a massive amount of innovations and frameworks over the past 3 to 4 years. Node.js, Backbone.js, Symfony, Composer, SASS, SCSS, LESS, Angular.js, d3.js, leaflet.js, Bootstrap, Responsive, CSS3, HTML5,... to name a few and then some.There's so much going on. And all are competing for my attention.

So, should I pick one or two and start toying around? Or should I methodically start learning them in the hopes that one day, I'll need to use one of those in my dayjob? Or should I just wait out until a challenge lands on my desk which hands me the opportunity to learn a new framework or language?

And then there's the elephant in the room: how long will what I've learned stay relevant? It's not a question of "if" but "when" will you're knowledge become outdated. Already, Drupal 8 will be a far cry from Drupal 7 as it is packed with loads of OOP/Symfony goodness and much, much more. There's not really any choice not to be involved if you're a Drupal developer. Even so, PHP is and has been the dominant server side scripting language on the Web, but these days Javascript is making vast strides to take a piece of the action. However, in our sector it's extremely hard to predict the future. There's no way to determine the exact expiration date of technology.

I think questioning one's competences is a balancing act most of us faces these days. But it isn't quite so problematic as it would seem. Picking a new framework to learn is a great thing to do. We should always expand our horizons and see what's happening elsewhere on the Web. However, we shouldn't be learning new frameworks merely because they *might* come in handy or because they're the latest fad. We should rather take stock of the new complexity and reassess our own motivations: What do I want to accomplish? Which part of the Web do I really love to build? What interests me and tickles my curiosity? But above all, always ask yourself: What furthers me in my own personal growth?

In the end, the one thing we should avoid is getting paralysed by the sheer breadth of knowledge out there and realise that we just can't learn and understand it all anymore.

I haven't updated this blog a lot in the last 8 weeks. I was transitioning between jobs and I focussed on getting used to new routines while losing some others. I've joined XIO as a Drupal developer. We're a small team based in Ghent but we build and breathe Drupal. I'm now almost 4 weeks with them and I'm having a blast working for them so far.

Part of the new routine is having my daily commute extended. I have quite some time and space to actually do stuff on the train and starting this week, I'm going to make good use of it. I'm amazed to see how much I've accomplished so far: I've released a bugfix version 7.x-1.x-alpha4 of Commerce Product Manager and I've done the odd contribution to the Link module in Drupal 8.

The other day, I had to tweak the "Add to cart" feature which comes out of the box with Drupal Commerce. Adding and tweaking features in Commerce can be a bit daunting. The package contains a fully fledged API with several subsystems to handle orders, products, checkout,... It's easy to get lost if you are new. This article explores this use case and the particularities I had to go through.

The challenge

I was involved in a webshop project. The challenge is to place the product price next to the quantity dropdown form element in the "Add to cart" form, separated with a multiplier sign. The end result should look like: price x quantity. Why? Because visually relating price and quantity makes for good UX. Check out this example of how the end result is supposed to look.

One might think it's easy to build something similar, but don't get fooled. It's trickier then things might appear. Let's take a look at the Field UI in the administration backend and see how things are actually set up by Commerce. Open up the default view mode of the corresponding product display node type for the product type you want to edit. Here, we control the display of the different fields shown on the product page. The Field UI not only shows you the fields from the product display node type, but also those inherited from the product entity. Including "Commerce price" and "Product variations" fields.

The hairy problem here is that price is a separate field. It's not an actual part of the add to cart form. The HTML rendered by Commerce also makes a structural divide between the form and the price. Even more so, the displayed price can be changed on the fly through an AJAX call! A product display is an aggregate of products. Each product has its' own SKU and attributes (color, size,...) which determine the price. So you want that skirt in a yellow bubble pattern instead of plain red? Well, you'll get a more expensive proposition, if you choose that option from the "Pattern" attribute dropdown. These product attributes are part of the form, yet if you change them, the price field will be dynamically updated too! Whatever we do, we don't want to break this intermingled functionality.

So, how can we nudge the price into the form in a controlled, sensible way?

Solutions

CSS

Without touching any PHP, you could work your way around this with some clever CSS. Since content and style should be separated, and HTML should only define structure in a document (i.e. product detail page), dealing with this through CSS sounds like a good way to go. Getting the price into the form might prove to be a long stretch though. You'll need to break out your positionising kungfu and you might spend a lot of time debugging in different browsers, devices,...

JQuery

Get your JQuery toolbelt out. With some search and replace actions in the DOM we could fit the price right into the cart HTML which makes for easier styling. However, we take a risk here of breaking the attribute based price refresh, if we are not too careful. Besides, it's a very lazy hack: the Drupal Way is not the fastest road in this case, but it's still the correct and least error-prone way of doing things.

The Drupal Way

So, first some code. I'll explain in a bit.

/**
* Alter the add to cart form
*/
function mymodule_form_commerce_cart_add_to_cart_form_alter(&$form, &$form_state) {
  // Are we coming from a node/x page or the attribute refresh AJAX callback?
  // Depending on the context: fetch the product display object from the URL or
  // form_state context.
  $entity = NULL;
  if (isset($form_state['triggering_element'])) {
    $entity = $form_state['context']['entity'];
  }
  else {
    $entity = menu_get_object();
  }

  if (isset($entity)) {
    global $language;
    $view_mode = 'full';
    $entity_type = 'node';
    $bundle = $entity->type;
    $wrapper = NULL;
    $product = NULL;
    $langcode = $language->language;

    // Create a container and move the quantity form field into it
    $quantity = $form['quantity'];
    unset($form['quantity']);
    $form['quantity']['#datatype'] = 'integer';
    $form['pricing'] = array(
      '#type' => 'container',
    );
    $form['pricing']['quantity'] = $quantity;
    $form['pricing']['quantity']['#weight'] = 0;

    // Let's get the referenced product entity
    $instances = field_info_instances($entity_type, $bundle);
    $reference_view_mode = $entity_type . '_' . $view_mode;

    // Loop through product reference fields to see if any exist on this entity
    // bundle that is either hidden or displayed with the Add to Cart form display
    // formatter.
    foreach (commerce_info_fields('commerce_product_reference', $entity_type) as $field_name =>
    $field) {
      if (isset($instances[$field_name])) {
        // Load a wrapper for the entity being viewed.
        if (empty($wrapper)) {
          $wrapper = entity_metadata_wrapper($entity_type, $entity);
        }

        // Find the default product based on the cardinality of the field.
        if ($field['cardinality'] == 1) {
          $product = $wrapper->{$field_name}->value();
        }
        else {
          $product = $wrapper->{$field_name}->get(0)->value();

          // If the product is disabled, attempt to find one that is active and
          // use that as the default product instead.
          if (!empty($product) && $product->status == 0) {
            foreach ($wrapper->{$field_name} as $delta => $product_wrapper) {
              if ($product_wrapper->status->value() != 0) {
                $product = $product_wrapper->value();
                break;
              }
            }
          }
        }
      }

      if (!empty($product) && $instances[$field_name]['settings']['field_injection']) {
        // Add the display context for these field to the product.
        $product->display_context = array(
          'entity_type' => $entity_type,
          'entity' => $entity,
          'view_mode' => $reference_view_mode,
          'language' => $langcode,
        );

        $product_field_name = 'commerce_price';
        $field_instances = field_info_instances('commerce_product', $product->type);
        $product_field = $field_instances[$product_field_name];
        if (!isset($product_field['display'][$reference_view_mode])) {
          $reference_view_mode = 'default';
        }

        // Only prepare visible fields.
        if (!isset($product_field['display'][$reference_view_mode]['type']) ||
          $product_field['display'][$reference_view_mode]['type'] != 'hidden') {
          $content = field_view_field('commerce_product', $product, $product_field_name,
            $reference_view_mode, $langcode);
          $output = render($content);

          $form['pricing']['price'] = array(
              '#markup' => $output,
          );

          if ($field['cardinality'] != 1) {
            // Construct an array of classes that will be used to theme and
            // target the rendered field for AJAX replacement.
            $classes = array(
                'commerce-product-field',
                drupal_html_class('commerce-product-field-' . $product_field_name),
                drupal_html_class('field-' . $product_field_name),
                drupal_html_class(implode('-', array('node', $entity->nid, 'product',
                  $product_field_name))),
            );

            $form['pricing']['price']['#prefix'] = '<span class="price-operator">&times;</span>
              <div class="' . implode(' ', $classes) . '">';
            $form['pricing']['price']['#suffix'] = '</div>';
            $form['pricing']['price']['#weight'] = 20;
          }
        }
      }
    }
  }
}

In essence, I just alter the commerce_cart_add_to_cart_form() function (Part of the Cart (commerce_cart) module and add the commerce price field as an extra element to the form. There are two hurdles I have to take: (i) Retrieving the price from the referenced product entities (ii) Rebuild the price field in such a way that the AJAX dynamics won't break.

The correct price is retrieved in commerce_product_reference_entity_view() (Product Reference module) which is actually an implementation of hook_entity_view. The logic in the hook implementation does exactly what I want to do. I dissected what I needed to display only one field - the commerce price - and reapplied that code in my own form alter function. Now, I need to muddle with the code to make it all fit

Since a hook_form_alter function is context unaware, I had to retrieve the calling product display node from the context and pass it to the code. I'm assuming we are on a detail page here, so that's why I use menu_get_object() to retrieve the node. But that's not enough. There's the issue of an AJAX call. I knew that Drupal rebuilds the entire form, calling all the form alter functions, whenever someone chooses a different option from an attribute dropdown. The entire code would be run again, but menu_get_object() wouldn't work since the call is not coming from a node page but a special url called by javascript. Luckily, checking the "triggering element" property allows me to discern how my code gets called. The product display node object is conveniently stored in $form_state['context']['entity']. This way, I'm pretty sure I'll always end up with the current product display node entity fully loaded and available.

Next up, I need to attach the price to the form and make it work together with the quantity. It's a two step proces. First, I'll move the quantity into a container. Like this:

// Create a container and move the quantity form field into it
$quantity = $form['quantity'];
unset($form['quantity']);
$form['quantity']['#datatype'] = 'integer';
$form['pricing'] = array(
  '#type' => 'container',
);
$form['pricing']['quantity'] = $quantity;

There is a slight problem here. The commerce_cart_add_to_cart_form_validate() function assumes the existence of $form['quantity']['#datatype'], but I'm breaking the structure. The validator will start to fail because of that. That's why I've added the expliciet $form['quantity']['#datatype'] = 'integer'; Yes, it's a bit of a hack, but it works. If you *really* want to do it the clean way, then you would have to replace the standard validation callback with a copy of the function changing the reference to the new location of the quantity field.

Rendering the price field and adding it to the form happens over here:

$content = field_view_field('commerce_product', $product, $product_field_name, 
  $reference_view_mode, $langcode);
$output = render($content);

$form['pricing']['price'] = array(
  '#markup' => $output,
);

Done right, the $classes variable should contain all the information necessary to render the exact same HTML context as the regular commerce price field would have. This is necessary since the AJAX javascript relies on those. I used the #prefix and #suffix properties to add the containing div's. I also added the × operator to the #prefix.

$form['pricing']['price']['#prefix'] = '<span class="price-operator">&times;</span>
  <div class="' . implode(' ', $classes) . '">';
$form['pricing']['price']['#suffix'] = '</div>';

Wrap up

Although I achieved my goal, I'm left with a bit of mixed feelings about this. I'm happy I pulled it off using the Drupal Way. I didn't reinvent the wheel with lazy hacks in JQuery and CSS. I did it through a nice form alter. Just the way the Drupal API intends it. Yet, I had to reïmplement a lot of existing logic in a separate function to make sure nothing breaks. That's plain wrong when you live by code reuse. It's also a consequence of the trade off made by the Commerce architects separating price from form in two separate Field API fields. The flexibility of a toolbox comes before the adaptability towards a very specific use case.

Due to popular demand, I've added a RSS feed. It's located at http://www.colada.be/articles/feed. It's a great example of what you can do with the Views 3 module.

A quick rundown of what I did:

1. Install & enable the Views module
2. I created a new view with a new page display showing all the articles on the site and a block display for the homepage
3. I added a new display of type "Feed"
4. Configured the path: /articles/feed
5. Configured the "Attach to" option and attached the feed display to the other two displays.
6. Saved the view and admired my handywork on the homepage.

Because it's so easy to create new feeds with Views, it's a low hanging fruit to do so if it's in the specs of your project. If you're a client, project manager,... and your project contains a news, blog,... feature which gets frequent updates, feeds are worth considering. A RSS feed is an extra channel which allows you to broadcast your content to a wider audience.

On an older incarnation of this site, I installed the Feedburner module to keep tabs on the usage of the feed, but according to TechCrunch and various other source, Google is phasing Feedburner out.

Back in the mid-naughties, blogs came under heavy attack from comment spammers. It was a blog authors' nightmare. Before long, your well gardened blog would become filled with comments promoting enhancing medicine and what-not. Spammers use the generated links to get a higher indexing rating from searchbots in an effort to maximize their margins. At the time, there were only a few solutions like primitive CAPTCHA's and their ilk. Then, Automattic introduced Akismet. This centralized service analyses content and tells you whether it's spam or not. Soon, WordPress core shipped with an Akismet plugin.

Although Akismet did stop spam from hitting the frontpage of my blog, you still had to cleanse the moderation queue on a daily basis. Not something I really enjoyed doing. I yearned for those very early blogging days when I didn't have to worry about comment spam.

Then, in 2008, Dries Buytaert introduced us to Mollom. This is a service Akismet alike, but with a twist: it combines text analysis and CAPTCHA's in an elegant solution. The base proposition: content can't necessarily be classified as either ham or spam, there's a grey zone. Form input is sent to Mollom for scrutinization. If a clear distinction can be made, the system will inform the client whether the content is spam or ham. Mollom will respond with a CAPTCHA when unsure how to deal with the submitted content. It's up to the visitor (commenter,...) to prove that they are not an automated spambot. As the system receives feedback during each step of the process, it's able to improve itself over time as more content is processed.

Being fed up with spam, this looked like something very promising! When Mollom went public beta, there was only a basic Drupal module available. In my enthusiasm, I started reverse engineering the module and the API. I wanted the same spam stopping power for WordPress. I didn't know how much work it would take to build something similar and I certainly hadn't any experience building something of the same scale. I was just a rookie hobbyist programmer with a serious itch. But oftentimes, that's all it takes to build something great.

After a week or so browsing code and reading the white paper again and again, I got stuck: the code and the service just weren't documented enough to make sense for a third party developer. That's when I e-mailed Dries. To my own surprise (I didn't know him back them), he was very cooperative and with his first response, he sent me a draft of the entire API specification. That kind of openness was a great motivator to continue working on the plugin. Over the next year, I build, tweaked and released new versions and prototypes in an iterative process. The best part was that as I worked on the plugin, I was able to provide feedback to Mollom. Mentioning where the documentation was off, or I received different results then what I expected allowed them to improve their service. So development was actually a two way process.

Then development stalled after several releases.

I moved on to other interests (Drupal!), went through several personal challenges (moving about a lot) and lost interest in blogging for a good while. The company went on to expand its' architecture, enhance its' API's and improve the Drupal module. I didn't have the time to catch up on all that. And finally, the plugin itself became an unmaintainable mess of code. Things didn't look that well.

In late 2011, I picked up development again. The API got implemented in a separate PHP class. Eventually, I decided to do a complete rewrite of the plugin. Development had become my day job by then, which meant that I could apply my new found knowledge and experience in this project. During 2012, I silently moved forward. I ported more functionality from the Drupal module to the WordPress plugin.

Developing in a different architecture comes with a lot of mismatch problems. One of the hardest things is that functionality in WordPress is implemented in a less abstract and componentized way as it is in Drupal. WordPress, after all, is (still) less an application framework. First and foremost, it's a publishing platform. One of the major problems is the lack of a centralised Form API which plugins can implement. This means that I can only go so far as to protect the comment form implementing specific hooks in WordPress' comment system. It also means that I had to write a HMAC based authentication component to prevent replay attacks of the CAPTCHA form, a feature included in Drupal's Form API.

As we've moved into the new year, so does development of the plugin over at Github. The base system is there and provides the same mechanics as the previous versions of the WordPress plugin did. My next goal is to iron out all major bugs so I can start alpha testing on my own personal blog. That's the first real test of the plugin. There's still a lot that needs to be done. A few major features are waiting to be implemented, and, of course, a lot of polishing. I'm quite optimistic though. If I can keep up with the current rate of progress, I can actually see this completely through.

If only I knew what I was getting myself involved with back in 2008. :-)

Subscribe to Older writings