Creating your own custom entity in Drupal and using the entity module's API

Tags: Tech Blog Published:

Drupal is a powerful content management system that allows you to create some pretty cool websites, it achieves this by allowing users to be able to create different entity types that allow them to interact with each other.

An easy way to think of this is that:

  • An entity type is the base class
  • A field attached to an entity is like the property/variable of the class
  • And the entity is an object or instance of the base class

Drupal comes with pre installed entity types such as nodes and taxonomy terms (Drupal content). However the downside to such entities is that they come with a lot functionality, which in some cases can be a bit of overkill if you just want to be able to create your own basic piece of functionality. **Who uses that preview function for every website???**

This is where you can build your own custom entity using the entity API module:

The entity API provides functionality that can make your life easier by ensuring that your custom entity can interact with Drupal.

All you need to do is follow the below steps:

Step 1 - Creating your folder and file structure

  • You need to save your project in site/all/modules/custom/project_name.
  • In this folder named project_name you need to create a project_name.info file that contains all your projects information.
  • project_name.install file that contains a hook_schema() function.
  • project_name.module file that contains the necessary hooks() to enable your entity type to be able to interact with Drupal.
  • Any .inc files where you want to be able to extend the entity modules classes.

Step 2 - Completing your project_name.info file

You need to ensure you include the following information about your project in your project_name.info file

name = an example entity description = a custom entity that can be used to interact with Drupal core = 7.x version = 7.x-0.x dependencies[] = entity dependencies[] = views files[] = project_name_entity.inc (and any other inc files you want to associate with your project)

Step 3 - Completing your project_name.install file - This is basically telling Drupal to build the necessary tables for where your entity's data will be stored

Below is an example of how to implement Drupal's "hook_schema()" function and create a table that has the following structure

<?php
        /**
         * Implements hook_schema()
         *
         * @param null
         *
         * @return array
         */
        function project_name_schema(){

            $schema[‘project_name’] = array(

                ‘description’ => ‘The main table that will store your entities data’,

                    ‘fields’ => array(
                        ‘project_name_id’=> array(
                            ‘description’ => ‘Primary key for project’,
                            ‘type’ => ‘serial’,
                            ‘not null’ => TRUE,
                            ‘unsigned’ => TRUE,
                        ),
                        ‘nid’=> array(
                            ‘description’ => ‘Foreign key to a related node’,
                            ‘type’ => ‘int’,
                            ‘not null’ => TRUE,
                            ‘unsigned’ => TRUE,
                        ),
                    ),

                    ‘indexes’ => array(
                        ‘nid’ => array(‘nid’),
                     ),

                    ‘unique keys’ => array(
                        ‘project_name_id’ => array(‘project_name_id’)
                    ),

                ‘foreign keys’ => array(

                    ‘node’ => array(
                        ‘table’ => ‘node’,
                        ‘columns’ => array(‘nid’,’nid’),
                    ),
                ),

                ‘primary key’ => array(‘project_name_id’),
            );

            return $schema;

        }
?>

Step 4 - Completing your project_name.module file. The final step.....this is where the fun starts and you can use the entity module's extremely useful API. It saved me loads of time!

Firstly you need to define your entities property information using hook_entity_info()

The below function contains information about which classes can be used to allow your entity to interact with certain Drupal features.

  • The entity class key should contain a class name that gives your entity access to additional methods that allow it to integrate with Drupal better than Drupal's default classes. In most cases you would use the entity class, however in this example we want to extend this class to change our entity's default uri. The entity class is the base class for entities.
  • The controller class key should contain a class name that allows your entity to communicate with your database effectively. This usually will be the EntityAPIController however we will extend this class with our own customEntityController. The EntityAPIController extends the DrupalDefaultEntityController class. It contains more 'create, read, update and delete (CRUD)' functionality.
  • The views controller class key should contain the class name EntityDefaultViewsController. This will allow the custom entity to be able to work with Drupal views. We do not need to extend this class.
  • The base table should contain the name of your database table you created in your schema_hook. For this tutorial it will be project_name.
  • The access callback key should contain the function name which will handle the entities permissions.
  • The uri callback should contain the callback function entity_class_uri so that your entity can be identified.
  • The admin callback key should contain an array as the value with the following keyed values path and controller classPath defines the url where users visit to view the entities admin screen and controller class with the value EntityDefaultUIController provides the default controller for providing us with a UI.
  • The entity keys key needs to contain an array that describes how the field api can intergrate with the entity type. It holds the properties that act as identifiers for the entities. It should contain the primary id of the property.
  • The module key needs to contain the modules name. This basically defines which module has created the entity type
<?php
        /**
         * Implements hook_entity_info().
         * @param null
         * @return array
         */
        function project_name_entity_info() {

          $info = array();

          $info[‘custom_entity’] = array(

            ‘label’ => ‘My custom entity’,

            ‘entity class’ => ‘customEntity’,

            ‘controller class’ => ‘customEntityController’,

            ‘views controller class’ => ‘EntityDefaultViewsController’,

            ‘base table’ => ‘project_name’,

            ‘access callback’ => ‘project_name_access_callback’,

            ‘uri callback’ => ‘entity_class_uri’,

            ‘admin ui’ => array(

                ‘path’ => ‘admin/project_name’,

                ‘controller class’ => ‘EntityDefaultUIController’,

            ),

            ‘entity keys’ => array(

              ‘id’ => ‘project_name_id’,

            ),

            ‘module’ => ‘project_name’,

          );

          return $info;
        }
    
?>

Now we have informed Drupal about our entity types we now need to add in some more functions that specifically describes the properties for the entity type you are creating. The hook_entity_property_info function allows us to do this. As a result views will be able to recognise the custom entity. It should be set up like below:

<?php
    /**
     * Implements hook_entity_property_info().
     * @param null
     * @return array()
     */
    function project_name_entity_property_info() {

      $info = array();

      $info[‘custom_entity’][‘properties’][‘nid’] = array(
        ‘label’ => t(‘Node id’),
        ‘description’ => t(‘Node id of custom entity’),
        ‘type’ => ‘node’,
        ‘schema field’ => ‘nid’,
      );

      return $info;
    }
?>

Notice how 'node' has been used as the type. If you are using users and taxonomy terms just set the type to be either user or taxonomy_term(remember these are entity types)

We now need to create your access callback function. See below on how to implement this:

<?php
    /**
     * function that defines an access callback for custom entities.
     *
     * @param $op
     *
     * @return boolean
     */
    function project_name_access_callback($op) {

      return true;

    }
?>

To be able to edit your custom entity you need to create the following form. As shown below.

<?php
    /**
     * form for adding / editing a custom entity.
     *
     * @param $form
     * @param $form_state
     * @param $custom_entity
     *
     * @return array
     */
    function custom_entity_form($form, &$form_state, $custom_entity = NULL) {

      $form[‘nid’] = array(
        ‘#title => t(‘Related node id’),
        ‘#type’ => ‘textfield’,
        ‘#default_value’ => isset($custom_entity->nid) ? $custom_entity->nid : ‘’,
        ‘#required’ => TRUE
      );

      $form[‘#validate’][0] = ‘validate_custom_entity’;

      $form[‘submit’] = array(
        ‘#type’ => ‘submit’,
        ‘#value’ => isset($custom_entity->project_name_id) ? t(‘Update entity’) : t(‘Save entity’),
        ‘#weight’ => 50
      );

      return $form;

    }
?>

You will notice that there is a validate_custom_entity callback function defined against the form. Validating your custom entities are important as it ensures data integrity

<?php
    /**
     *
     * Validate function to ensure nid entered are correct entity types
     *
     * @param $form
     * @param $form_state
     *
     * @return void
     */
    function validate_custom_entity($form, &$form_state){

      $node_id = $form_state[‘values’][‘nid’];

      if(node_load($node_id) === FALSE){

        form_set_error(‘nid’, t(“The node id you have entered is not a node”));

      }

    }
?>

A function should be created to allow us to save our entities. This can be achieved by doing the following.

<?php
    /**
     * Submit handler for the custom entity form.
     *
     * @param $form
     * @param $form_state
     *
     * @return void
     */
    function custom_entity_form_submit($form, &$form_state) {

      $entity = entity_ui_form_submit_build_entity($form, $form_state);
      $entity->save();
      drupal_set_message(t(‘The stream: @id has been saved.’, array(‘@id’ =>$entity->project_name_id)));
      $form_state[‘redirect’] = ‘admin/project_name’;

    }
?>

To be able to view single entities you need to implement the hook_menu() function and the callback function associated.

<?php
    /**
     * Implements hook_menu().
     * @param null
     * @return array()
     */
    function project_name_menu(){

        $items[‘project_name/id/%’] = array(
          ‘title’ => t(‘custom entity entry’),
          ‘page callback’ => ‘view_custom_entity’,
          ‘page arguments’ => array(2),
          ‘access arguments’ => array(‘access content’)
        );


        return $items;
    }

    /**
     * View handler for the entity
     *
     * @param $id
     *
     * @return entity object
     */
    function view_custom_entity($id){

      $entity = entity_load(‘custom_entity’, array($id));
      drupal_set_title(‘Entity: ‘.$entity[$id]->project_name_id);
      $output = entity_view(‘custom_entity’, array($entity[$id]));
      return $output;

    }
?>

We are nearly there, however as described above we need to be able to extend the entity class and the entityApiController class and override some methods.

Below shows how we extend the entity class. We need to override the default Uri method to ensure we can define a custom path that will take us to an entity. Which is picked up by our hook_menu().

<?php
    /**
     * stream entity class extending the Entity class
     */

    class customEntity extends Entity {

      /**
       * Change the default URI from default/id to project_name/id
       */
      protected function defaultUri() {
        return array(‘path’ => ‘project_name/id/’ . $this->identifier());
      }

    }
?>

Below shows how we can extend the entityApiController class and override the buildContent method. This allows us to be able to control the format of how we can view our single entity instance.

<?php
    /**
     * Extending the EntityAPIController for the custom_entity entity.
     */
    class customEntityController extends EntityAPIController {

        public function buildContent($entity, $view_mode = ‘full’, $langcode =NULL, $content = array()) {

        global $base_url;

        $build = parent::buildContent($entity, $view_mode, $langcode, $content);

        $build[‘custom_entity_id’] = array(
            ‘#type’     => ‘markup’,
            ‘#markup’   => check_plain($entity->project_name_id),
            ‘#prefix’ => ‘Entity type id: <div id =”project_name_id_’.$entity->project_name_id.’“>’,
            ‘#suffix’ => ‘</div>’,
            );

        $build[‘nid’] = array(
            ‘#type’     => ‘markup’,
            ‘#markup’   => check_plain($entity->nid),
            ‘#prefix’ => ‘Node id associated: <div id =”project_name_id_’.$entity->nid.’“><a href=”’.$base_url.’/node/’.$entity->nid.’“>’,
            ‘#suffix’ => ‘</a></div>’,
            );

        return $build;

        }

    }
?>

So there you have it, a way in which you can develop your own custom entity types in Drupal, that can be compatible with views and have admin functionality to be able to manage them!