[Drupal] How to create custom entity programatically in Drupal 7
https://www.zyxware.com/sites/default/files/styles/user_image/public/default_images/index.png?itok=2YmREnrP
BY premshankar.pb
4 years ago
Drupal
2 comments comment

An entity is a useful abstraction to make grouping together of fields. Here I am giving you a brief view about how the custom entities created, adding extra fields, adding bundles etc all things attached to entity.

Understanding Entity system

In Drupal, entity acts like a ORM(Object Relation Mapper) that maps and binds some data fields in different tables to a single back boned structure. Entities have properties and extra fields. Properties are those keeps the signature data of that entity (such as status, updated, created, user id), extra fields are those which are stored in separate tables,which has instances of that particular entity. Also another term in entity creation are bundles.

Before going deep into the entity , just look the node structure. Node is an entity, where articles, blog are its bundles. If you add more fields in the structure of the article, a new table named field_data_[field_name] would be created. Node table is the base table of the entity 'node' where created, updated, status, uid etc are its properties.

Start building my custom entity

Let us make sample custom entity. I want to create 2 entities Training and booking.

The structure of the entities are as follows:

Training (2 bundles: main and sub task)

Properties:

  • id
  • created
  • updated
  • type
  • title

Fields

  • description
  • mentor

Booking
Properties:

  • id
  • created
  • updated
  • uid
  • training_id

Fields

  • training_place
  • team_size
  • training_order

Lets start creating entity

Put the code in the .module file.


/*
 * Implements hook_entity_info()
 */
function mymodule_entity_info() {
  $services['training_program'] = array(
    'label' => t('Training program Entity'),// label of the entity to be displayed in the views and all
    'entity class' => 'Entity',// default entity class
    'controller class' => 'EntityAPIController',//default controller class, later we can extent this class to make properties of this entity
    'base table' => 'training_program_data',// base table for saving the entity properties
    'fieldable' => TRUE,//set to true, if extra fields are to be attached to the entity
    'entity keys' => array(// we are setting the entity keys and the bundle as the property named 'type' of this entity. 
      'id' => 'training_data_id',
      'label' => 'name',
      'bundle' => 'type',
    ),
    'view modes' => array(
      'full' => array(
        'label' => t('Default'),
        'custom settings' => FALSE,
      ),
    ),
    'bundle keys' => array(
      'bundle' => 'type',
    ),
    'bundles' => array(// define the bundles in the entity
      'main' => array(
          'label' => t('main', array(), array('context' => 'training main bundle'))
        ),
      'subtask' => array(
          'label' => t('subtask', array(), array('context' => 'training subtask bundle')),
        ),
    ),
    'module' => 'your_module',
    'metadata controller class' => 'TrainingMetadataController',// entity controller class extension in which properties can be created.
    'views controller class' => 'EntityDefaultViewsController',
    'load hook' => 'training_load', //custom entity load function, which is described below
  );
  $services['training_booking'] = array(
    'label' => t('Training Booking Entity'),
    'entity class' => 'Entity',
    'controller class' => 'EntityAPIController',
    'base table' => 'training_booking_data',
    'fieldable' => TRUE,
    'entity keys' => array(
      'id' => 'booking_data_id',
      'label' => 'name',
    ),
    'view modes' => array(
      'full' => array(
        'label' => t('Default'),
        'custom settings' => FALSE,
      ),
    ),
    'module' => 'your_module',
    'metadata controller class' => 'BookingMetadataController',
    'views controller class' => 'EntityDefaultViewsController',
    'load hook' => 'booking_load',
  );
  return $services;
}

Then we are writing some support custom functions to load, create our custom entities


/**
 * Loads a Training by ID.
 */
function training_load($training_id) {
  if (empty($training_id)) {
    return FALSE;
  }
  $trainings = training_load_multiple(array($training_id), array());
  return $trainings ? reset($trainings) : FALSE;
}
/**
 * Loads multiple training by ID or based on a set of matching conditions.
 */
function training_load_multiple($training_ids = array(), $conditions = array(), $reset = FALSE) {
  if (empty($training_ids) && empty($conditions)) {
    return array();
  }
  return entity_load('training_program', $training_ids, $conditions, $reset);
}

/**
 * Returns an initialized training object. Used to create an entity of type training_program.
 */
function training_new() {
  return entity_get_controller('training_program')->create();
}

/**
 * Loads a Training Booking by ID.
 */
function booking_load($booking_id) {
  if (empty($booking_id)) {
    return FALSE;
  }
  $bookings = booking_load_multiple(array($booking_id), array());
  return $bookings ? reset($bookings) : FALSE;
}

/**
 * Loads multiple booking by ID or based on a set of matching conditions.
 */
function booking_load_multiple($booking_ids = array(), $conditions = array(), $reset = FALSE) {
  if (empty($booking_ids) && empty($conditions)) {
    return array();
  }
  return entity_load('training_booking', $booking_ids, $conditions, $reset);
}

/**
 * Returns an initialized booking object. Used to create a entity of type training_booking.
 */
function booking_new() {
  return entity_get_controller('training_booking')->create();
}

Creating Base table

So next step is that we have to create a base table for entity. In this base table the entity id along with the properties saved.
So we created the base table in the hook schema. So create this base table in your install file.


  
/**
 * @file
 * Install, update and uninstall functions for the node module.
 *
 * Implements hook_schema().
 */
function mymodule_schema() {
  $schema['training_program_data'] = array(
    'description' => 'The base table for training data.',
    'fields' => array(
      'training_data_id' => array(
        'description' => 'The primary identifier for a training data.',
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'created' => array(
        'description' => 'The Unix timestamp when the data was created.',
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
      ),
      'updated' => array(
        'description' => 'The Unix timestamp when the data was most recently saved.',
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
      ),
     'title' => array(
        'description' => 'The title of this data, always treated as non-markup plain text.',
        'type' => 'varchar',
        'length' => 255,
        'not null' => TRUE,
        'default' => '',
      )
    ),
    'primary key' => array('training_data_id'),
  );
  $schema['training_booking_data'] = array(
    'description' => 'The base table for greyocean training booking data.',
    'fields' => array(
      'booking_data_id' => array(
        'description' => 'The primary identifier for a training booking data.',
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'uid' => array(
        'description' => 'The {users}.uid that owns this data; initially, this is the user that created it.',
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
      ),
      'created' => array(
        'description' => 'The Unix timestamp when the data was created.',
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
      ),
      'updated' => array(
        'description' => 'The Unix timestamp when the data was most recently saved.',
        'type' => 'int',
        'not null' => TRUE,
        'default' => 0,
      ),
      'status' => array(
        'description' => 'Integer - (0 ­ submitted, 1 ­ ready for analysis, 2 ­ ready for report, 3 ­ finalised).',
        'type' => 'int',
        'not null' => TRUE,
        'default' => 1,
      ),
      'training_data_id' => array(
        'description' => 'Foreign key training data',
        'type' => 'int',
        'not null' => TRUE,
        'default' => 1,
      ),
    ),
    'foreign keys' => array(
      'training_data_id' => array(
        'table' => 'training_program_data',
        'columns' => array('training_data_id' => 'training_data_id'),
      ),
    ),    
    'primary key' => array('booking_data_id'),
  );
  return $schema;
}

Creating properties

Then we have to create the properties for entities and set them to each base table field. Create a .info.inc file and put the property creation code inside this file. Don`t the forget to include the filename inside the .info file.


/**
 * Extend the defaults.
 */
class TrainingMetadataController extends EntityDefaultMetadataController {
  public function entityPropertyInfo() {
    $info = parent::entityPropertyInfo();
    $properties = &$info[$this->type]['properties'];
    $properties['created'] = array(
      'label' => t('training created'),
      'schema field' => 'created',
      'getter callback' => 'entity_property_getter_method',
      'setter callback' => 'entity_property_verbatim_set',
      'required' => TRUE,
      'description' => t('created date of the assessment data.'),
    );
    $properties['updated'] = array(
      'label' => t('training updated'),
      'schema field' => 'updated',
      'getter callback' => 'entity_property_getter_method',
      'setter callback' => 'entity_property_verbatim_set',
      'required' => TRUE,
      'description' => t('updated date of the assessment data.'),
    );
    $properties['title'] = array(
      'label' => t('training title'),
      'schema field' => 'title',
      'getter callback' => 'entity_property_getter_method',
      'setter callback' => 'entity_property_verbatim_set',
      'required' => TRUE,
      'description' => t('title of the rating data.'),
    );        
     $properties['type'] = array(
      'type' => 'text',
      'label' => t('Type'),
      'description' => t('The human readable name of the bundle type.'),
      'setter callback' => 'entity_property_verbatim_set',
      'getter callback' => 'entity_property_getter_method',
      'options list' => 'mymodule_type_options_list',
      'required' => TRUE,
      'schema field' => 'type',
    );    
    return $info;
  }
}

/**
 * Extend the defaults.
 */
class BookingMetadataController extends EntityDefaultMetadataController {
  public function entityPropertyInfo() {
    $info = parent::entityPropertyInfo();
    $properties = &$info[$this->type]['properties'];
    $properties['uid'] = array(
      'label' => t('booking uid'),
      'schema field' => 'uid',
      'getter callback' => 'entity_property_getter_method',
      'setter callback' => 'entity_property_verbatim_set',
      'required' => TRUE,
      'description' => t('uid of the user created the assement.'),
    );   
    $properties['training_program_id'] = array(
      'label' => t('reference training id'),
      'schema field' => 'training_data_id',
      'getter callback' => 'entity_property_getter_method',
      'setter callback' => 'entity_property_verbatim_set',
      'required' => TRUE,
      'description' => t('training reference ID.'),
    );
    $properties['created'] = array(
      'label' => t('booking created'),
      'schema field' => 'created',
      'getter callback' => 'entity_property_getter_method',
      'setter callback' => 'entity_property_verbatim_set',
      'required' => TRUE,
      'description' => t('created date of the assessment data.'),
    );
    $properties['updated'] = array(
      'label' => t('booking updated'),
      'schema field' => 'updated',
      'getter callback' => 'entity_property_getter_method',
      'setter callback' => 'entity_property_verbatim_set',
      'required' => TRUE,
      'description' => t('updated date of the assessment data.'),
    );
    return $info;
  }
}

Add extra fields

Next step is to create extra fields.

Here in training entity, there are 2 bundles 'main' and 'subtask'. So each extra fields in the 'training' entity should have 2 instances each for main and sub-task. Instances are those which creates entity strains along with the bundle to a field. All the field instances created are recorded in the table field_config_instance. Also all the extra fields created are stored in the table field_config.


/**
 * @file
 * Define field and field instances, attach them to entity bundles.
 */
function mymodule_get_training_field_data() {
  $training_fields = array(
    'training_description' => array(
      'field' => array(
        'field_name' => 'training_description',       // field name
        'entity_types' => array('training_program'),  // entity type to which the field is attached
        'cardinality' => 1,                           // number of values to be saved at a time
        'type' => 'text',                             // type of field
        'settings' => array('max_length' => 255),
      ),
      'instance1' => array(
        'field_name' => 'training_description',
        'label' => 'Training Description',
        'entity_type' => 'training_program',
        'bundle' => 'main',
        'description' => 'The description about Training.',
      ),
      'instance2' => array(
        'field_name' => 'training_description',
        'label' => 'Training Description',
        'entity_type' => 'training_program',
        'bundle' => 'subtask',
        'description' => 'The description about Training.',
      ),
    ),
    'mentor' => array(
      'field' => array(
        'field_name' => 'mentor_description',
        'entity_types' => array('training_program'),
        'cardinality' => 1,
        'type' => 'text',
        'settings' => array('max_length' => 255),
      ),
      'instance1' => array(
        'field_name' => 'mentor_description',
        'label' => 'Training Mentor Description',
        'entity_type' => 'training_program',
        'bundle' => 'main',
        'description' => 'The description about Training Mentor.',
      ),
      'instance2' => array(
        'field_name' => 'mentor_description',
        'label' => 'Training Mentor Description',
        'entity_type' => 'training_program',
        'bundle' => 'subtask',
        'description' => 'The description about Training Mentor.',
      ),
    ),    
  );
  return $training_fields;
}

/**
 * Create fields and field instances for training entity type.
 */
function mymodule_create_training_fields() {
  // Define field and field instances.
  $training_fields = mymodule_get_training_field_data();
  foreach ($training_fields as $field_data) {
    $field = $field_data['field'];
    // Create field if not exists.
    if (!field_read_field($field['field_name'], array('include_inactive' => TRUE))) {
      field_create_field($field);
    }
    $instance1 = $field_data['instance1'];
    // Create field instance if not exists.
    if (!field_read_instance($instance1['entity_type'], $instance1['field_name'], $instance1['bundle'], array('include_inactive' => TRUE))) {
      field_create_instance($instance1);
    }
    $instance2 = $field_data['instance2'];
    // Create field instance if not exists.
    if (!field_read_instance($instance2['entity_type'], $instance2['field_name'], $instance2['bundle'], array('include_inactive' => TRUE))) {
      field_create_instance($instance2);
    }
  }
}

function mymodule_get_booking_field_data() {
  $booking_fields = array(
    'booking_order' => array(    //here booking order is an reference field of another entity commerce_order. SO type = 'entityreference'
      'field' => array(
        'field_name' => 'booking_order',
        'entity_types' => array('training_booking'),
        'cardinality' => 1,
        'type' => 'entityreference',
        'settings' => array(
          'target_type' => 'commerce_order',
        )
      ),
      'instance' => array(              
        'field_name' => 'booking_order',
        'label' => 'booking order',
        'entity_type' => 'training_booking',
        'bundle' => 'training_booking',
        'required' => TRUE,
        'description' => 'The order of booking.',
      ),
    ),
    'team_size' => array(
      'field' => array(
        'field_name' => 'team_size',
        'entity_types' => array('training_booking'),
        'cardinality' => 1,
        'type' => 'number_integer',
      ),
      'instance' => array(
        'field_name' => 'team_size',
        'label' => 'Size of team',
        'entity_type' => 'training_booking',
        'bundle' => 'training_booking',
        'description' => 'Size of the team.',
      ),
    ),
    'training_place' => array(
      'field' => array(
        'field_name' => 'training_place',
        'entity_types' => array('training_booking'),
        'cardinality' => 1,
        'type' => 'text',
        'settings' => array('max_length' => 255),
      ),
      'instance' => array(
        'field_name' => 'training_place',
        'label' => 'Training place',
        'entity_type' => 'training_booking',
        'bundle' => 'training_booking',
        'description' => 'Training Place of the bookie.',
      ),
    ),
  );  
  return $booking_fields;
}

/**
 * Create fields and field instances for training entity type.
 */
function mymodule_create_booking_fields() {
  // Define field and field instances.
  $booking_fields = mymodule_get_booking_field_data();
  foreach ($booking_fields as $field_data) {
    $field = $field_data['field'];
    // Create field if not exists.
    if (!field_read_field($field['field_name'], array('include_inactive' => TRUE))) {
      field_create_field($field);
    }
    $instance = $field_data['instance'];
    // Create field instance if not exists.
    if (!field_read_instance($instance['entity_type'], $instance['field_name'], $instance['bundle'], array('include_inactive' => TRUE))) {
      field_create_instance($instance);
    }
  }
}

We have to call these field creation functions on hook_enable. Put the below code in the .module file.


/**
 * Implements hook_enable().
 */
function mymodule_enable() {
  mymodule_fields_extra();
}

function mymodule_fields_extra() {  
  module_load_include('inc', 'mymodule', 'includes/mymodule.fields');
  mymodule_create_training_fields();
  mymodule_create_booking_fields();
}

Deleting the extra fields while uninstall

We should create code to delete all the extra fields while uninstalling the module. For that call hook_unistall() in the .install file.


/**
 * Implements hook_uninstall().
 */
function mymodule_uninstall() {
  $training_field_instance = field_info_instances('training_program', 'training_program');
  foreach ($training_field_instance as $field_data) {
    field_delete_instance($field_data);    
  }
  $booking_field_instance = field_info_instances('training_booking', 'training_booking');
  foreach ($booking_field_instance as $field_data) {
    field_delete_instance($field_data);    
  }
}

Create/load entity data

To create an entity:

$training = training_new(); //an object for the entity training_program is created
$time = time();
$training->title = 'training_title';
$training->created = $time;
$training->updated = $time;
$training->type = 'main'; // set the bundle

//and now we go for adding the values for extra fields
global $user;
$language = isset($user->language)?$user->language:'und';
$training->training_description[$language][0]['value'] = 'training detail';
$training->mentor[$language][0]['value'] = 'mentor name';
entity_save('training_program', $training);                    //save the entity

$booking = booking_new();                                      //an object for the entity training_booking is created
$time = time();
global $user;
$booking->title = 'training_title';
$booking->created = $time;
$booking->updated = $time;
$booking->uid = $user->uid;
$training = training_load(1); //load training entity of training_id = 1,
$booking->training_id = $training->training_data_id;

//and now we go for adding the values for extra fields

$language = isset($user->language)?$user->language:'und';
$booking->training_place[$language][0]['value'] = 'training place';
$booking->team_size[$language][0]['value'] = 10;

$booking->booking_order[$language][0]['target_id'] = 102; // this is an entity reference field, so value is saved as 'target_id'

entity_save('training_booking', $training);//save the entity

That`s it!!!!!! your custom entity is fully functioning. If you want to know more about this topic, please get in touch with us.


RELATED ARTICLE

/themes/custom/zyxpro_light/images/placeholder.png
close

on 01st January 2008 / by webmaster
We have added a few new modules to our site recently. Open Source Web Development Drupal Blogging Free Software Drupalgive Leave a reply Your email address will not be published. Required fields are marked * Sean (not verified) access_time 06 Dec 2019 - 13:49 Hi there, I uploaded the files, enabled the modules to use SU, and now I see the link, but there is no icon... How can I fix this? Thanks, Sean webmaster access_time 06 Dec 2019 - 13:49 In reply to Icon doesn't show by Sean (not verified) What do you see when you view the source? Search for stumblethis_button and you should be able to see the code for the image and the URL. Then you should be able to troubleshoot from there. Juicy Couture Addict (not verified) access_time 06 Dec 2019 - 13:49 thanks for the post. would love to hear more of you. by the way, drupal's really popular nowadays as it has easy and fast features. you agree with me? thanks. Add new comment
/themes/custom/zyxpro_light/images/placeholder.png
more_horiz
close

on 16th January 2008 / by webmaster
Most webmasters do not realize this, but a lot of the content on lot of websites can be accessed from multiple URLs. A simple example would be where www.example.com and example.com leads to the same page. This is a fatal mistake in Search Engine Optimization and search engines penalize you for duplicate content. The correct configuration would be where the above two urls will lead you to the same page but example.com will redirect you to www.example.com with a 301 (Moved permanently) status which will not result in search engines penalizing the page. It is very easy to configure 301 redirects using Apache .htaccess file and the process is the same for a Drupal installation also. Web Development Drupal SEO Drupal Planet Leave a reply Your email address will not be published. Required fields are marked * Anonymous (not verified) access_time 06 Dec 2019 - 13:50 Hello. I'm trying to make example.com show as www.example.com, and I'm running into difficulties. I'm on Apache 2.0 and using the following lines in my httpd.conf file: RewriteEngine on RewriteCond %{HTTP_HOST} ^xxxxxxxxxx\.com$ [NC] RewriteRule ^(.*)$ http://www.xxxxxxxxxx.com/$1 [L,R=301] When I go to http://xxxxxxxxxx.com, I get http://www.xxxxxxxxxx.com (as expected). However, when I go to http://xxxxxxxxxx.com/node/1 (it's a Drupal site), I get a 404 thrown and the URL changes to xxxxxxxxxx.com/var/www/drupal/node/1. Same thing with www.xxxxxxxxxx.com/node/1. Any suggestions? I want to run without Drupal's .htaccess file (instead incorporating these calls into my httpd.conf file). webmaster access_time 06 Dec 2019 - 13:50 In reply to Rewrite including filesystem path by Anonymous (not verified) I think the problem is with the base path which results in the redirection to /var/www/ part. The best approach I would think is to start with drupal htaccess and then strip out parts and move to httpd SNVC (not verified) access_time 06 Dec 2019 - 13:50 This is definitely a good guide. Thanks for this. wellyson access_time 06 Dec 2019 - 13:50 This is really nice and helpful. Add new comment
/themes/custom/zyxpro_light/images/placeholder.png
close

on 07th January 2008 / by webmaster
We have volunteered to take up the maintenance of a very useful Drupal module - Search404. As of today we are the official maintainers of this very useful Drupal module. We know that this is going to be a challenge for us, being a young company and with a young team. But we do feel that it is our responsibility to give back to the Drupal community at least some part of what it has given us. Open Source Web Development Drupal Leave a reply Your email address will not be published. Required fields are marked * ian douglas (not verified) access_time 06 Dec 2019 - 13:50 I notice your updates on the Drupal modules page has some patch files, but they are patches for an older version of the 5.x branch of search404. Do you have any expectation on when a version for Drupal 6 will be ready? webmaster access_time 06 Dec 2019 - 13:50 In reply to when will search404 for drupal 6 be ready? by ian douglas (not verified) Hi Ian, We are currently working on moving Search404 to Drupal 6. The port has already been done and we should have a release up on drupal.org by tomorrow or worst case by monday. Cheers Anoop John Team Zyxware Dejan (not verified) access_time 06 Dec 2019 - 13:50 In reply to when will search404 for drupal 6 be ready? by ian douglas (not verified) I think that's been up for a while... did you check the download page? Add new comment
Leave a reply
Your email address will not be published. Required fields are marked *

Filtered HTML

  • Web page addresses and email addresses turn into links automatically.
  • Allowed HTML tags: <a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type='1 A I'> <li> <dl> <dt> <dd> <h2 id='jump-*'> <h3 id> <h4 id> <h5 id> <h6 id>
  • Lines and paragraphs break automatically.
The content of this field is kept private and will not be shown publicly.
CAPTCHA This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.

author-image
Topto (not verified)
access_time 06 Dec 2019 - 16:39

You added the t() function in the wrong place in the TrainingMetadataController class

$properties['updated'] = array(
'label' => 'training updated',
'schema field' => t('updated'),