Sunday 18 March 2012

Drupal 7 views vs entityFieldQuery pt1

Introduction

This is the first part of a discussion in which I am hoping to layout a few basic thoughts, get some feedback and then come back with later posts and concrete code examples and design patterns. I am finding it difficult to easily start discussions with other Drupal developers on this topic as it is too easy to slip into a "code everything yourself vs views" discussion. In Drupal 6 Views have become almost engrained as the standard way to solve many problems, some problems that can and should be solved in different ways in Drupal 7 and onward.

Increasingly though I am seeing in comments and posts elsewhere discussion around the new contender for solving some of these problems. A contender that felt like the antidote to the overloading of the 'views metaphor' as soon as I started working with Drupal 7 although I still haven't worked out all the wrinkles. That contender is the EntityFieldQuery.

A very good article was written by Neil Hastings on Building Energy.gov without Views.

I still appreciate views

I am not anti-views, they are very useful, packed full of features, extended by many other modules etc. Often views are the obvious choice for admin tools/reports for users and can be used to give more power to non-technical users. Combined with other modules such as bulk operations or editable fields for example; you can achieve something very quickly that would take a lot of work otherwise.

What I have and do struggle with sometimes is the incredibly complex and 'if it works don't knock it' solutions that can be wrangled out of views. If it takes a fresh developer over an hour trying to work out what is going on with a complex views solutions before they can start debugging a problem, then something is wrong. Once you get beyond a certain point the time you save with simpler views can be quickly lost debugging quirks or behavior that is not what you expect. As you move into contextual filters, relationships etc. there is a lot to learn, using views from within code and the views api, the learning curve can be quite steep.

One thing that views are very good at is a quick route from raw content type to selection and display, there is nothing directly in the EntityFieldQuery that maps to this, so how does it rival views......

Abastraction for data retrieval

EntityFieldQuery rivals views as an abstraction layer for retrieving data. Fundamentally you need to select entity ids (node ids when dealing with node based content) based on some criteria. The database structure Drupal uses doesn't make this very elegant at the database level, tables for each (field and revision of the entity). Selecting your data with sql queries also ties you into a particular storage method.

No code here yet, just a discussion, suppose you have a small content type (a few fields) that needs to undergo some kind of selection process and then get displayed somewhere in a page (perhaps a carousel or similar). A view on the content type that gets displayed as a block would be a typical approach.

Alternatively with very little code you can provide a block with hook_block_info(), specify the block content as the output of a function within hook_block_view(). The function that outputs the content can call on EntityFieldQuery for the node selection and assemble the render array (or return no content if appropriate).

On its own this approach doesn't sound very interesting, however now you have a lot of flexibility to apply randomization, filtering based on taxonomies, caching of the content simply, or basing your cache key on weirdness (that makes sense of your requirements), filters based on things that may be hard to apply in a view, combining selection from multiple queries etc. etc. Maybe drive something from a context using the context module or a cookie the user carries or ... and not worry about whether any of these things integrate with (or can be integrated with) views. You may have to take a simple requirement and add complexity later.

What is more there are lots of ways to customize the output, not least the introduction of custom node view modes in Drupal 7 these can help when building your render array this is key for displaying the different aspects of content in various different ways.

Enterprise level solutions

Ultimately you can define your own entities and extend EntityFieldQuery (it is a PHP class) to meet some of your specific requirements. Defining your own implementation of the EntityFieldControllerInterface or extending the default implementation gives all the potential power you need. Even moving your entity storage to MongoDB

Summary

Although it seems clear that the role of views is (or should be) changing for Drupal 7 onwards, there is not a lot of guidance or clear examples of best practice. I am hoping to generate a little discussion and find out/confirm what the best practice should be.

Sunday 4 March 2012

Break out from Drupal (from the inside)

Quick post: sometimes you (or someone you are working with) may need to break out from Drupal, and generate a special page perhaps based on some existing php code that has been adapted or similar, but that is now pulling data from drupal.

The following code is a brief example of an approach that allows foo.php (in this example) to have full access to any to any variables you may create from querying Drupal and perhaps pick up arguments from the url. foo.php in this case is now responsible for generating the page output.

The use of drupal_exit(); in this case still allow modules to implement exit hooks.


/**
 * Implements hook_menu()
 */
function my_module_menu() {
 
  $items = array();
 
  $items['foo'] = array(
    'title' => t("Foo"),
    'type' => MENU_NORMAL_ITEM,
    'page callback' => 'my_module_foo_page',
    'page arguments' => array(),
  );
 
  return $items;
}

/**
 * My module foo page callback
 */
function my_module_foo_page($arg1 = false, $arg2 = false, $arg3 = false) {
  
  //do some regular Drupal stuff here, populate some variable etc. 

  drupal_add_http_header('Content-Type','text/html');
  include_once('foo.php');
  drupal_exit();
}