By Daniel Sipos

Build Your Own Custom Entities in Drupal – Implementation

By Daniel Sipos

Build Your Own Custom Entities in Drupal

Welcome back to the second part of this tutorial in which we explore the world of custom entities in Drupal. If you haven’t already, I strongly recommend you read the first installment, but let’s do a short recap nonetheless.

In the previous article we’ve defined the schema for our entity type and registered it with Drupal. We’ve also overridden the EntityAPIController to build up the display for our entities.

In this part of the tutorial we will continue and talk about a few other cool things we can do with entities in Drupal. First, we’ll quickly set up the pages where we can display the individual project entities. Next, we will build a straightforward but very powerful admin interface to manage them. Then, we will make our entity type fieldable so we can add fields through the UI. And finally, we’ll expose it to Views so we can create proper listings of project entities.

If you want, you can follow along with the source code from the first branch of the Git repository, or take a peek into the second branch which contains all the code we will cover today.

Individual entity pages

The first thing we’ll do is create the pages for displaying individual project entities. We’ll start by adding a new item to our hook_menu() implementation:

  $items['project/%'] = array(
    'title' => 'Project',
    'page callback' => 'demo_view_project',
    'page arguments' => array(1),
    'access arguments' => array('access content'),

We are registering a path (project/id) and a callback function (demo_view_project()) to which we pass the wildcard URL argument (the ID of the project). As for access, anybody with the access content permission can see the page.

Next, let’s write the said callback function (keep in mind this is a simple example just for demonstration purposes):

 * Callback function for displaying the individual project page
function demo_view_project($id) {

  $projects = entity_load('project', array($id));
  $project = $projects[$id];

  $output = entity_view('project', array($project));

  return $output;


This is again very simple: we load the entity with the ID passed from the URL, we set the title of the page, run the entity object through entity_view() and return it as page output. We’ve covered these Entity API concepts last time when we listed our projects. You can now clear the cache and navigate to project/1 and you should see the project with the ID of 1. If you see the project name twice, don’t worry, this will become clear in the next section when we let Drupal know which one is the default URI callback for the project entities.

Admin user interface

Now that we can display the individual entities, let’s leverage the power of the Entity API module to set up a quick admin user interface to manage them. There are a few simple steps we need to take for this.

First, let’s edit our hook_entity_info() implementation for our entity type and add the following (I’ll explain everything after):


'access callback' => 'demo_access_callback',
'uri callback' => 'entity_class_uri',
'admin ui' => array(
  'path' => 'admin/projects',
  'controller class' => 'EntityDefaultUIController',


And replace this line:

'entity class' => 'Entity',

With this:

'entity class' => 'ProjectEntity', 

With these modification, we do 4 things:

  1. We specify an access callback function for the entity type. We’ll need this for the admin UI and we’ll declare the callback function in a minute.
  2. We set the uri callback to the default one provided by the entity class (I will come back to this at point 4).
  3. We set the admin ui information: path to the UI page and the controller class that will handle it. EntityDefaultUIController is the default UI class that comes with Entity API and it is declared in the file.
  4. We change the name of the entity class for this entity type to one that does not exist yet. We will create it now by extending the previous one so that we can override its defaultUri() method:

     * Project entity class extending the Entity class
    class ProjectEntity extends Entity {
       * Change the default URI from default/id to project/id
      protected function defaultUri() {
        return array('path' => 'project/' . $this->identifier());

As you can see, we are basically changing that path to the individual project entity returned by this class method. When the time comes, I will point out why this was handy but this will be the default one I mentioned at point 2. Now let’s quickly also declare our access callback function mentioned at point 1:

 * Access callback for project entities.
function demo_access_callback($op, $project = NULL, $account = NULL) {
  if ($op == 'view' || $op == 'update' || $op == 'create' || $op == 'delete') {
    return TRUE;
  else {
    return FALSE;

As you can see, this is not much of an access callback function as it returns true for everything. Here you will normally perform proper access checks but for our demonstration purposes it works just fine.

Now there is one last thing we need to do to make use of our admin interface: declare a simple add/edit form for the project entity and its submit handler:

 * Form definition for adding / editing a project.
function project_form($form, &$form_state, $project = NULL) {

  $form['name'] = array(
    '#title' => t('Project name'),
    '#type' => 'textfield',
    '#default_value' => isset($project->name) ? $project->name : '',
    '#required' => TRUE,

  $form['description'] = array(
    '#title' => t('Project description'),
    '#type' => 'textarea',
    '#default_value' => isset($project->description) ? $project->description : '',
    '#required' => TRUE,

  $form['deadline'] = array(
    '#title' => t('Project deadline'),
    '#type' => 'textfield',
    '#default_value' => isset($project->deadline) ? $project->deadline : '',
    '#required' => TRUE,

  $form['submit'] = array(
    '#type' => 'submit', 
    '#value' => isset($project->id) ? t('Update project') : t('Save project'),
    '#weight' => 50,

  return $form;

 * Submit handler for the project add/edit form.
function project_form_submit($form, &$form_state) {
  $project = entity_ui_form_submit_build_entity($form, $form_state);
  drupal_set_message(t('The project: @name has been saved.', array('@name' => $project->name)));
  $form_state['redirect'] = 'admin/projects';

For the deadline field, I went with a simple text field just to make this demonstration quicker. Normally, you’d want to use the contrib Date module for a nice widget and then save that value as a timestamp. But this will do for our example.

As for the rest, there is nothing major going on. A few things you should keep in mind though. In order for the Entity API to pick up automatically on the fact that this is the form for the project entities, the declaring function name needs to be exactly that. If the entity type was called chocolate, the function name would have been chocolate_form(). You’ll also notice that we pass the $project entity as a third parameter. This will be handled by the Entity API for the edit form. And finally, in the submit handler, you notice the use of the entity_ui_form_submit_build_entity() function. This is a great helper that will take all the values from the $form_state array (the user input) and populate the entity properties with them.

And that’s it. We can clear the cache and navigate to where we should see a table of our project entities, with links to view, edit and delete them + an additional one for adding new ones.

Earlier I mentioned that I will let you know why we had to override the Entity class and change the default URI for the entity type. The link Drupal placed behind the project names are generated based on this information. So as long as the path exists, you should now have a full blown CRUD system + pages for displaying individual entities. Not bad.



The next thing we’ll see is how to make our entities fieldable. Let’s again edit the hook_entity_info() implementation and add some more information there:


'fieldable' => TRUE,
'bundles' => array(
  'project' => array(
    'label' => t('Project'),
    'admin' => array(
      'path' => 'admin/projects',


In the first line we tell Drupal that this entity type should use fields and in the following lines we define a bundle they can be attached to. Bundles are to what we attach fields in Drupal and any entity type can have multiple bundles. For example, in a fresh Drupal install, the node entity has the article and page bundles (content types).

We define a single one, called project and specify some basic information. Now if you clear the cache and go to admin/projects you should see two new tabs at the top of the page resembling what you see when you add fields to regular content types. Now we can add fields to our entity.

You’ll also notice that after you add some fields through the UI and try to edit or add a project, you don’t see the fields in the form. That’s because we need to include them. Edit the project_form() function we wrote earlier and right before the submit element, paste the following:

field_attach_form('project', $project, $form, $form_state);

Using the Field API field_attach_form(), we attach to the form all the necessary elements declared through the UI. Now go to the form again and you should see the new fields. And even on the individual project entity display page the field values get rendered as you configure them through the UI.

Exposing the entity type to Views

The final thing we’re going to look at is how to expose our project entities to Views. The most basic thing we can do in this respect is to edit the hook_entity_info() implementation and specify another controller class that would handle this functionality:


'views controller class' => 'EntityDefaultViewsController',


Now you can just clear the cache and create a new View. Make sure you choose Project over Content and you’ll have all the entity properties available. One thing to note is that Views cannot interpret the kind of values we are storing in those properties. The deadline field, for instance, is actually a date and not a simple integer. So if you add that field, it will treat it as a simple numeric value.

To fix this, we need to implement hook_entity_property_info() and specify exactly what kind of data the project properties are storing:

 * Implements hook_entity_property_info().
function demo_entity_property_info() {

  $info = array();

  $info['project']['properties']['id'] = array(
    'label' => t('Project ID'),
    'description' => t('The ID of the project.'),
    'type' => 'integer',
    'schema field' => 'id',

  $info['project']['properties']['name'] = array(
    'label' => t('Project name'),
    'description' => t('Name of the project.'),
    'type' => 'text',
    'schema field' => 'name',
  $info['project']['properties']['description'] = array(
    'label' => t('Project description'),
    'description' => t('Description of the project.'),
    'type' => 'text',
    'schema field' => 'description',
  $info['project']['properties']['deadline'] = array(
    'label' => t('Deadline'),
    'description' => t('Project deadline.'),
    'type' => 'date',
    'schema field' => 'deadline',

  return $info;

As you can see, it’s a big nested array situation in which for the project entity we map its properties to their schema column and specify what type of data is found there. Now if you clear the cache and add these fields in your View, Views will know what values are stored in the properties. For the deadline field, it will use its date handler this time and transform the timestamp into a date string for us.

An important thing to keep in mind when implementing this hook: you cannot do it only for one property. As soon as you implemented and described one of your properties, you need to add the rest as well. Otherwise Views won’t show the rest at all anymore.


We’ve reached the end of our exploration of Drupal entities. And although we covered quite a lot, there is loads more you can do to perfect and customize your entity types. We’ve seen in this part how to integrate with Views, how to add fields to our entities and even how to create an admin interface for them. But there’s more you can do: view modes, revisions, etc.

We’ve written some example code and created some awesome functionality. And yes, from a strictly “boy this is a long read” perspective, it was quite intense and complex. But if you take a step back and think about what we achieved with what I guarantee you is almost no code at all, you’ll understand why the Drupal entity system is so great.

  • Paul

    is there a way to show the table fields in the manage fields or display fields tabs, e.g. so you could re-order them for display? Is there a way to have these fields recognised by DS (display suite) module?

  • Guest

    Hey there,

    You can try with the hook_fields_extra_fields() hook.

  • fred

    Hi when i go to with on my site then i got an error “No acces”

    • Edward Xu

      add “access callback” => TRUE to hook_entity_info()

      • BetoSoft

        If you do access callback true (in this case) you will get a wsod. I did it when i first read this tutorial, only for write my entity faster, and lost my time figuring out what was happening. Unfortunately I can’t explain this behavior, but it happened to me.

  • fred

    Another question. When I have a project and project tasks, can i make then one entity with 2 bundels and can i join the information in a view with a common item? Or must I make two entities?

  • Daniel Harper


    I’ve implemented a custom entity and was just wondering how I can include my hard coded fields in the mage display options so that the wright can be changed with ones added by the field api. Currently my entity has a coded title and body field which I would like to include in the manage display tab.

    Also the admin UI on mine doesn’t quite work it shows the tabs but also renders a grey table with an empty row and an add entity link which has the wrong URL

    Great Post it filled in a lot of missing information for me.

    Cheers Dan

  • duffyj007

    Great Tutorial!

  • Wow man, very good tutorial.

    I also was reading other articles to learn who to create my own Drupal entities by code and yours is the better from far. Very good structured adding each new stuff in a separate step, not like others, that starts with a huge array and you don’t know which key is necessary for what thing.

    Thank you very much.

  • Ruth Ameel

    Hi, super tutorial, it really works very well!!
    I’ve tried to use this custom module with Rules. I can create a new entity with rules, but when I try to set a data field, I get the error ‘The selected data property doesn’t support writing.’ Any Ideas on this?

  • Ruth Ameel

    Hi Daniel,

    I just found the answer to my previous question. Apparently, I needed to add the ”setter callback’ => ‘entity_property_verbatim_set’,’ for each item in my hook_entity_property_info() . Works also in Rules now. Thanks again for this great tutorial!

  • Oliver Coleman

    Thank you so much for posting this! I spent hours trying to work out how to add fields to an entity with a single default bundle. Every other example showed how to do this in the context of multiple bundles configured by the user. You’d think it wouldn’t be too hard to work out from these how to do it for a single default bundle, but lo, it surely wasn’t obvious what needed to be removed and what kept, even after reading the various bits of documentation on Entity API. And, what Juan said.

  • russellt

    An excellent tut. There are so many different ways to do things in Drupal. This is a well-explained, focussed walk through of entities.

  • Thanks for this. I’ve been diving into Drupal stuff headfirst lately and these articles were definitely wayyyy more helpful than a lot of the stuff I’ve found out there. Thanks again!

  • AAG

    Very good tutorial about entities in Drupal, the best ever.
    Other tutorials do not have the educational quality that has this.

    Thank you very much for sharing.

  • Emmanuel Buckshi

    Great job. Thanks a lot for doing this.

  • Wyckham Seelig


    Thank you very much for these two excellent tutorials. I think that the most helpful aspect of your material was that it provided a complete, end to end, example of the entire process of creating and testing an entity.

    Again, thank you very much.

  • Wyckham Seelig


    A question.

    As I said below, these were excellent tutorials. I actually implemented them in my own little module and they all worked.

    I noticed something, though, that puzzles me greatly. As long as the module that I created [mostly copied from your code] on my site remains enabled, I see the entities that I created. However, if I disable that module the entities go away! The table of individual instances of the entity remains in my database, and if I re-enable the module, everything still works. BUT, without the module, it looks like the entity type that I created is gone. I’m obviously missing something, but I cannot figure out what it is. Is there some step where you save the definition of the entity type??


Get the latest in PHP, once a week, for free.