Understanding Drupal’s EntityFieldQuery

Introduction

When building complex web apps, you’ll eventually have to interact with a database. To retrieve data in Drupal one can use the database abstraction layer provided, which requires some SQL knowledge to be used properly. From Drupal 7 EntityFieldQuery API is provided, which lets you fetch information about entities from Drupal without actually building SQL queries. In this article, let’s see how we can use the EntityFieldQuery API to fetch data from Drupal and use it in our modules.

The EntityFieldQuery class

The EntityFieldQuery class is used to find entities in Drupal, which match some conditions. The EntityFieldQuery is present in the includes/entity.inc file in your Drupal installation.
This class has different methods to specify the entity type and certain conditions based on which we can filter which data we want to fetch. We will see those methods in detail throughout this article.
The basic usage, which you will follow while using EntityFieldQuery will be, first and foremost, creating an object of this class. Once the object is created you will add some conditions to it and then call the execute method to fetch the results.

The general template is as follows

$entityquery = new EntityFieldQuery();
/// Set some conditions   
$result = $query->execute ();

We are now going to create a Drupal module which will install 3 new Drupal node types: Product, Movies and Books and a block in which we will display our results. You can see how to create a node type in this article

The code for the module is

entityquery.info

name = entityquery
description = Shows how to use entity query to fetch data from drupal
package = Entity Query Example
core = 7.x

entityquery.install

<?php
/**
 * Implement hook_install().
 */
function entityquery_install() {
    node_types_rebuild();
    $types = node_type_get_types();
    node_add_body_field($types['product']);
    node_add_body_field($types['movies']);
    node_add_body_field($types['books']);
}

entityquery.module

<?php
/**
 * Implement hook_node_info()
 */
function entityquery_node_info() {
    return array(
        'product' => array(
            'name' => t('Product'),
            'base' => 'product',
            'description' => t('You can define new Products here'),
            'has_title' => TRUE,
            'title_label' => t('Product title')
         ),
        'movies' => array(
            'name' => t('Movies'),
            'base' => 'movies',
            'description' => t('You can define new Movies here'),
            'has_title' => TRUE,
            'title_label' => t('Movie title')
         ),
        'books' => array(
            'name' => t('Books'),
            'base' => 'Books',
            'description' => t('You can define new Books here'),
            'has_title' => TRUE,
            'title_label' => t('Books title')
         )
    );
}

/**
 * Implement hook_form()
 */
function product_form($node, $form_state) {
    return node_content_form($node, $form_state);
}

/**
 * Implement hook_form()
 */
function movies_form($node, $form_state) {
    return node_content_form($node, $form_state);
}

/**
 * Implement hook_form()
 */
function books_form($node, $form_state) {
    return node_content_form($node, $form_state);
}

/**
 * Implement hook_block_info().
 */
function entityquery_block_info() {
  $blocks = array();

  $blocks['entityqueryblock'] = array(
    'info' => t('A block to display results from entityquery'),
  );

  return $blocks;
}

/**
 * Implement hook_block_view().
 */
function entityquery_block_view($block_name = '') {
  if ($block_name == 'entityqueryblock') {
    $content ='';
    $block = array(
      'subject' => t('A block to display results from entityquery'),
      'content' => $content,
    );
    return $block;
  }
}

Put this module in your modules folder and if everything has gone well you will be able to see the entityquery module in your module list as shown below.

Once you have installed the module you should be able to see the new node types also in the Add content section of your Drupal admin panel as follows

Executing a simple query using EntityFieldQuery

Once we have the basic module set up for using EntityFieldQuery, let’s start writing some queries to fetch data from Drupal. The first query we will write is to get all the nodes using EntityFieldQuery. Then we will use that to display the titles of the nodes in our block.

As stated earlier the first thing to do is to create an instance of EFQ. To fetch a type of entity you have to add the entity_type condition to it. In this case we want to fetch nodes so the code for it will be as follows:

$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'node');

The entity condition is set using the function entityCondition in which we are setting the entity type as node. Once we have set the entity condition, we are ready to execute the query. The node ids are returned in an array in the node key of the result. The complete code to display the nodes will be as follows:

$query = new EntityFieldQuery();
    $query
    ->entityCondition('entity_type', 'node');


    $result = $query->execute();
    $nodes = array();
    if (isset($result['node'])) {
      $nids = array_keys($result['node']);
      $nodes = node_load_multiple($nids);
    }

    $list = array();
    foreach ($nodes as $node) {
      $options = array('absolute' => TRUE);
      $url = url('node/' . $node->nid, $options);
      $list[] = '<a href='.$url.'>'.$node->title.'</a>';
    }

    $theme_args = array('items' => $list, 'type' => 'ol');
    $content = theme('item_list', $theme_args);

    $block = array(
      'subject' => t('A block to display results from entityquery'),
      'content' => $content,
    );
    return $block;

Now if you go and see your block you should be able to see all nodes in it as follows

You should now try to add different nodes like movies and books and check them being displayed in the block. In the above, code once we have the node ids from EntityFieldQuery, we load the nodes using node_load_multiple and display them.

Adding entity conditions to EntityFieldQuery

You can add entity conditions to show only particular types of nodes. If we want to display only “products” from the type of nodes, the query we will use is:

$query = new EntityFieldQuery();
    $query
    ->entityCondition('entity_type', 'node')
    ->entityCondition('bundle', 'product'); 

    $result = $query->execute();

Now if we check our block it will display only products:

We can even specify an array of node types to fetch multiple types of nodes using entityCondition. To fetch all products and movies from your database:

$query = new EntityFieldQuery();
    $query
    ->entityCondition('entity_type', 'node')
    ->entityCondition('bundle', array('product', 'movies')); 

    $result = $query->execute();

Adding property conditions to EntityFieldQuery

We can even add property conditions to the query. These would depend on the entity type you are querying for. In most cases, the property condition will be on the fields of the entity type you are querying for. You can, for example, query for nodes which are published, or are written by a particular user etc.

The query to show only published nodes using propertyCondition is as follows

  $query = new EntityFieldQuery();
    $query
    ->entityCondition('entity_type', 'node')
    ->propertyCondition('status', 1);

    $result = $query->execute();

Adding field conditions to EntityFieldQuery and ordering

The field conditions are specific to the fields which are present on the entity. So suppose we want to find all the products which have the word discount in their body – we can do it using the field condition. We can even order the results using the propertyOrderBy function.

If we want the products and movies which have “discount” in their body, arranged in descending order of their creation, the query will be as follows:

   $query = new EntityFieldQuery();
    $query
    ->entityCondition('entity_type', 'node')
    ->entityCondition('bundle', array('product', 'movies'))
    ->propertyCondition('status', 1)
    ->fieldCondition('body', 'value', 'discount', 'CONTAINS')
    ->propertyOrderBy('created', 'DESC');

    $result = $query->execute();

The output of this query will be as follows

Extending the EntityFieldQuery class

Sometimes, you might have to build the same query in many places. A good way to abstract that would be to extend the EntityFieldQuery class and create your own child class.

Suppose you want to build a query to get all active users in ascending order of their creation date:

class ActiveUsersEntityFieldQuery extends EntityFieldQuery {

  public function __construct() {

    $this
      ->entityCondition('entity_type', 'user')
      ->propertyCondition('status', 1)
      ->propertyOrderBy('created', 'ASC');

  }

}

Now you can use this query anywhere like so:

$activeUsers = new ActiveUsersEntityFieldQuery();
$result2 = $activeUsers->execute();

Conclusion

Many modules in Drupal will need you to fetch entity content from the database. One can directly use the Drupal database layer, but for that you have to have at least a working knowledge of SQL and it could be more prone to errors. The EntityFieldQuery class is a part of Drupal’s core and you can easily use it without dependency on other modules. Have fun creating your next Drupal module!

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • yarontal

    Nice quick overview of EFQ. Serves as a good intro for those willing to dive in.
    A couple of comments though;
    1. Please use the l() function for creating links instead of url() and manually putting it in de a element.
    2. Why not also show the possibilities with other entity types?

    • http://www.bitfalls.com/ Bruno Skvorc

      Thanks for the feedback! The errors will be corrected. Other entity types will likely be covered in subsequent articles.

  • Ramiz Sajid

    please tell me when we show the node blocks using entityfieldquery() how we provide hyperlink for record that fetch fron db using l() function..