Meteor is a JavaScript framework that enables developers to quickly create reactive, highly accessible applications. It can be an incredibly powerful tool, especially when paired with Drupal, which is a great content management system that makes it really easy to create well-structured content models and interact with data.

Why not combine the two and let Drupal provide Meteor with semantic data with Meteor rendering that data in a reactive way?

I’ve been playing around with this idea for some time now and have created a few tools that allow Drupal and Meteor to do just that. I recently presented on this topic at NYC Camp, and wanted to take a deeper dive into the ideas. Let’s take a look!

Toolset

There are a few modules I use to send data from Drupal to Meteor:

Mongosync allows one to define Drupal entities of a specific bundle to be synced to a given collection in Meteor. It listens to entity events such as insert, update, and delete, and syncs those actions against Meteor’s datastore. It is compatible with Drupal 7 and has been working with Drupal 8 beta.

Distill enables other modules to extract and format data from Drupal entities by providing a simple class structure for defining formatting schemas.

Mongosync invokes hooks before it tries to push data to MongoDB, and allows any hook implementations to return a processed version of the entity that should be inserted into MongoDB. Drupal entities are… complicated. And kind of weird. They contain a lot of data that’s only useful to Drupal, and there’s no need to send that data to MongoDB. Getting data out of a Drupal object can also be hard; to get a simple field value, you might have to do something like this:

// Drupal 8. $my_field = $entity->my_field->getValue(); $my_field_value = $my_field['value']; // Drupal 7. $value = $entity->my_field[LANGUAGE_NONE][$a_delta]['value']; // Or: $wrapper = entity_metadata_wrapper('node', $entity); $value = $object->my_field->value(); 1 2 3 4 5 6 7 8 9 10 // Drupal 8. $ my_field = $ entity -> my_field -> getValue ( ) ; $ my_field_value = $ my_field [ 'value' ] ; // Drupal 7. $ value = $ entity -> my_field [ LANGUAGE_NONE ] [ $ a_delta ] [ 'value' ] ; // Or: $ wrapper = entity_metadata_wrapper ( 'node' , $ entity ) ; $ value = $ object -> my_field -> value ( ) ;

This is kind of gross, especially if you have to do this for a bunch of fields, and have a lot of different types of entities that need different fields to be formatted. Distill solves this problem by abstracting the extraction and formatting process into a class system.

A Mongosync/Distill marriage module could consist of something like this:

/** * mongosync_meteor module for Drupal 8. * (Distill works on Drupal 7 too) */ use DrupaldistillDistill; use DrupaldistillDistillProcessor; use Drupaldistill_exampleDistillTestEntityProcessor; /** * Implements hook_mongosync_entity_insert(). */ function mongosync_meteor_mongosync_entity_insert_preprocess($entity, $type, $bundle) { // Create instance of processor. $processor = new DistillProcessor(); // Create instance of Distill. $distiller = new Distill($entity, $processor); $distiller->setField('field_user', 'user'); $distiller->setField('field_pizza', 'pizza_id'); $distiller->setField('field_address', 'address'); return $distiller->getFieldValues() + array('entity_id' => $entity->id()); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 /** * mongosync_meteor module for Drupal 8. * (Distill works on Drupal 7 too) */ use DrupaldistillDistill ; use DrupaldistillDistillProcessor ; use Drupaldistill_exampleDistillTestEntityProcessor ; /** * Implements hook_mongosync_entity_insert(). */ function mongosync_meteor_mongosync_entity_insert_preprocess ( $ entity , $ type , $ bundle ) { // Create instance of processor. $ processor = new DistillProcessor ( ) ; // Create instance of Distill. $ distiller = new Distill ( $ entity , $ processor ) ; $ distiller -> setField ( 'field_user' , 'user' ) ; $ distiller -> setField ( 'field_pizza' , 'pizza_id' ) ; $ distiller -> setField ( 'field_address' , 'address' ) ; return $ distiller -> getFieldValues ( ) + array ( 'entity_id' = > $ entity -> id ( ) ) ; }

This just invokes a Mongosync hook, and cleans up the entity being pushed to MongoDB. The data as processed by Distill in this function, when inserted into MongoDB would look something like this:

{ user: "Patrick Coffey", pizza_id: "1234", address: "3456 44th street Middle Earth TX, 78759" } 1 2 3 4 5 6 { user : "Patrick Coffey" , pizza_id : "1234" , address : "3456 44th street Middle Earth TX, 78759" }

Putting it all together

Once you have Mongosync installed and connected to Meteor, and set to sync a specific entity type, crank up Meteor and the mongo server you’ll be talking to, and save an entity. That entity should now exist in your MongoDB database, and should be available to Meteor. When you use that data in Meteor to build an interface, when the dataset changes (based on a Drupal action), the interface will react to that change, and update within the browser session accordingly.

Why?

The big question: Why do any of this? Just because you can do something doesn’t mean it’s a good idea. So why would anyone want to use Meteor to build reactive interfaces from Drupal data? Well, reactive interfaces are extremely useful as they add a lot of value to sites, especially if those sites are presenting rapidly changing datasets. Meteor makes it easy to make great reactive interfaces, Drupal is a great content management system. Together, they make a pretty formidable team.