Integrating entities with Views

For the past few months I've been working a lot with custom entities where I needed to do various Views integration. Views has always been something I've stayed away from for the most part, because in my mind it was terrible complex and customizing it was something reserved only for people like merlinofchaos. After sniffing around in the code and doing various integrations I've come to the conclusion that while Views internals is quite complex, it is also built in a very developer friendly way. This means that it's actually possible to do a lot of the needed integration without having to deal with the complexities of the Views internals.

The most basic Views integration

Using the entity module you can get a lot of basic Views integration for free. You can let the entity module describe your entity so you can create views that display entities with any of it's properties and using conditions like you would do on nodes. To do this you are required to do 2 things, when you define your entity using hook_entity_info.

function hook_entity_info() {
  $return = array(
    'entity_type' => array(
      'controller class' => 'EntityAPIController',
      ...
      'views controller class' => 'EntityDefaultViewsController',
    ),
  );

  return $return;
}

In the above code example I've outlined the two things needed for the basic views integration.

Step one is the controller class. If you're new to entities, the controller class is a PHP class that Drupal will used to do various things with your entity. Stuff like saving, updating, deleting and loading your entity will be handled by the controller class through the use of Drupal API functions like entity_save and entity_load. You don't need to use the EntityAPIController you can also use a custom subclass which is what I usually do.

Step two is adding the views controller class key to your entity definition. When defined, the EntityAPIController will use this class to define the entity to views by looking at the table defined in hook_schema. It makes a a good default definition, but fails for things like foreign keys to fx the user, or time stamps, used for things like when the entity was created or updated, which is treated as a number. There is two ways to deal with this, either you can subclass the views controller class to add in custom definitions, or you can use hook_views_data_alter. Of the two, I prefer the latter, since it makes it very easy to see what changes I've made and of the two it seems like the most simple to implement.

Getting views to display your custom data

Sometimes you store a machine name in the database that should be displayed as a human readable and translatable string. A normal use case for this, is storing a status, which could be stored as a machine name using a varcar or a number. Views will by default just pull out the raw data from the database, but if you help views a bit, you can make it transform that raw data into something you would expect want to see. This was something that took my a while to figure out, but after I was done, I was surprised how simple this task is. It can be done in two simple steps.

1. Define a field_handler

Field handlers is what views uses internally to handle a lot of things like defining theme functions and templates for the fields, creating tokens used in the views UI and a bunch of other things you don;t want to work about. It also defines a render function which is determines how the raw value is rendered. This is a very simple function that looks like this:

function render($values) {
  $value = $this->get_value($values);
  return $this->sanitize_value($value);
}

What we would like to do, is instead of returning a sanitized value, is to return a human readable value. To do this we need to create our own views field_handler, Since Views uses classes, it makes it very easy for us, since we just need to subclass that. To create the field_handler you would need to do some something like this,

class my_custom_views_handler_field extends views_handler_field {
  function render($values) {
    $value = $this->get_value($values);
    $san = $this->sanitize_value($value);
    $options = method_to_get_options();
    return isset($options[$san]) ? $options[$san] : $san;
  }
}

This code could live anywhere. The Views code standard is to place it in a file named the same as the class and include it via the .info file.

2. Tell views about our custom field_handler

Since we now have a custom Views field handler, we need to tell Views about it so it will actually use it. For this we need to use hook_views_data_alter. With it we can change the classes views will use for rendering the properties of the entity (SQL tables) we have defined (and even those defined by others)

Generally Views store all the data about entity like this

$data['base table']['column']['the_way_views_can_use_it']['handler'] = 'class_name';

Let me explain the different links in the above chain:

  • base table: This is the name of the table that holds the table which for entities is called base table.
  • column: The column in the table that holds that data, which usually corresponds to a property on the entity.
  • the_way_views_can_use_it: This is the way that views can use the property, filter: when Views use property as a filter, to determine what data to display, sort when Views use the property to sort the data results, argument: when Views use the property to load the entity (perform an SQL join) and field: when Views display the value itself.

In our case we are using the last, field, so the code above can be refined to look like this:

hook_views_data_alter(&$data) {
  $data['base table']['column']['field']['handler'] = 'my_custom_views_handler_field';
}

Comments

Thank you for this detailed post, that helped to clear thins up alot.

There seems to be very little info published on this topic.

Could you please elaborate on how the hook_views_data_alter call is implemented?

Does this live in my_custom_views_handler_field.inc that is called from the .info?

As with all hooks, it should be placed in the .module file of the module you create for this.

I have found that unless the module key is set in my implementation of hook_entity_info(), the entity is not available to views:

mymodule_entity_info() {
return array(
'myentity' => array(
'module' => 'mymodule',
'controller class' => 'EntityAPIController',
...
'views controller class' => 'EntityDefaultViewsController',
),
);
}

I am not sure if this was implied in your blog post by the ellipsis in the code, but I thought it was worth mentioning in case anyone else arrives here as a result of not being able to get their entities to show to views!

Got a little confused about the .module. Anyway I'll be Bookmarking this page for future reference. Thanks.

You might be interested to know that Entity now handles properties with an options list callback automatically and displays the corresponding label for the technical key in your database.

In the module views, how to create an rendered entity ? ( FORMAT - Show : Rendered entity).
As for the module taxonomy, user, node...

Add new comment