Making use of the admin UI

Last updated on
20 December 2016

Optionally, the entity API helps provide an administrative interface for managing entities, e.g. The UI for managing profile types of the Profile 2 module is built with that. The UI is intended to be used for exportable entities (such as one serving as bundle for another entity type). The base controller class for an entity serving as a bundle is EntityAPIControllerExportable.

To implement the UI, you'll have to at least:

  1. Specify the 'path' under 'admin ui', as documented for hook_entity_info(), as well as 'module' and 'access callback'.
  2. Define an 'access callback'.
  3. Create the add/edit/clone form by implementing ENTITY_TYPE_form() and its associated submission handler ENTIITY_TYPE_form_submit()

1. Specify an 'admin ui' and other special settings in hook_entity_info()

Your implementation of hook_entity_info() must have an 'admin ui' property which takes an array of further properties. At the very least in this, you must specify a 'path' where the admin UI page will be located. You should also declare the file that will hold your add/edit/clone form and submit function (which is defined in step 4).

You must also set the 'access callback' (and define this function, in the next step) and set 'module' with your module name.

Here is an example from profile 2:

 * Implements hook_entity_info().
function profile2_entity_info() {
  $return['profile2_type'] = array(
    //... usual hook entity info implementation.
    'access callback' => 'profile2_type_access',
    'module' => 'profile2',
    // Enable the entity API's admin UI.
    'admin ui' => array(
      'path' => 'admin/structure/profiles',
      'file' => '',
      // OPTIONAL- See "Customize the User Interface" below if you set.
      'controller class' => 'Profile2TypeUIController',
  return $return;

2. Define an access callback

This is the function named in 'access callback' property in the hook_entity_info() implementation, above. It should return TRUE if the provided user account should have access to administer your entity, and FALSE if not.

Again, an example from profile2.

 * Access callback for the entity API.
 * @param $op
 *   The operation being performed. One of 'view', 'update', 'create', 'delete'
 *   or just 'edit' (being the same as 'create' or 'update').
 * @param $profile
 *   (optional) A profile to check access for. If nothing is given, access for
 *   all profiles is determined.
 * @param $account
 *   (optional) The user to check for. Leave it to NULL to check for the global user.
 * @return boolean
 *   Whether access is allowed or not.
function profile2_type_access($op, $profile = NULL, $account = NULL) {
  return user_access('administer profile types', $account);

3. Implement the entity adding/editing form

The entity form must be defined by the function ENTITY_TYPE_form() so that it can be retrieved using entity_ui_get_form(). You must also implement a submit function for this form. Both may live in the include file specified for the 'admin ui' via the optional 'file' key and could look like the following example form - a shortened version of the profile2 type form:

 * Generates the profile type editing form.
function profile2_type_form($form, &$form_state, $profile_type, $op = 'edit', $entity_type = NULL) {

  if ($op == 'clone') {
    // Only label is provided for cloned entities.
    $profile_type->label .= ' ' . t('(cloned)');
    $profile_type->type = $entity_type . '_clone';

  $form['label'] = array(
    '#title' => t('Label'),
    '#type' => 'textfield',
    '#default_value' => isset($profile_type->label) ? $profile_type->label : '',
  // Machine-readable type name.
  $form['type'] = array(
    '#type' => 'machine_name',
    '#default_value' => isset($profile_type->type) ? $profile_type->type : '',
    '#disabled' => $profile_type->isLocked(),
    '#machine_name' => array(
      'exists' => 'profile2_get_types',
      'source' => array('label'),
    '#description' => t('A unique machine-readable name for this profile type. It must only contain lowercase letters, numbers, and underscores.'),
  $form['actions'] = array('#type' => 'actions');
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save profile type'),
    '#weight' => 40,
  return $form;

 * Form API submit callback for the type form.
function profile2_type_form_submit(&$form, &$form_state) {
  $profile_type = entity_ui_form_submit_build_entity($form, $form_state);
  // Save and go back.
  $form_state['redirect'] = 'admin/structure/profiles';

Note that using entity_ui_form_submit_build_entity() (or other entity_ui functions) in your submit handler means you must always get the form via entity_ui_get_form() rather than drupal_get_form(), so it takes care of including the required files for these helper functions.

Optional: Customize the User Interface

To customize the default UI, override the controller class as in the following example:

 * UI controller - may be put in any include file and loaded via the code registry.
class Profile2TypeUIController extends EntityDefaultUIController {

   * Overrides hook_menu() defaults.
  public function hook_menu() {
    $items = parent::hook_menu();
    $items[$this->path]['description'] = 'Manage profiles, including fields.';
    return $items;

See Also - Describes how to do this with the core API only.