t('Example Basic Entity'), // The controller for our Entity, extending the Drupal core controller. 'controller class' => 'EntityExampleBasicController', // The table for this entity defined in hook_schema() 'base table' => 'entity_example_basic', // Returns the uri elements of an entity. 'uri callback' => 'entity_example_basic_uri', // IF fieldable == FALSE, we can't attach fields. 'fieldable' => TRUE, // entity_keys tells the controller what database fields are used for key // functions. It is not required if we don't have bundles or revisions. // Here we do not support a revision, so that entity key is omitted. 'entity keys' => array( // The 'id' (basic_id here) is the unique id. 'id' => 'basic_id' , // Bundle will be determined by the 'bundle_type' field. 'bundle' => 'bundle_type', ), 'bundle keys' => array( 'bundle' => 'bundle_type', ), // FALSE disables caching. Caching functionality is handled by Drupal core. 'static cache' => TRUE, // Bundles are alternative groups of fields or configuration // associated with a base entity type. 'bundles' => array( 'first_example_bundle' => array( 'label' => 'First example bundle', // 'admin' key is used by the Field UI to provide field and // display UI pages. 'admin' => array( 'path' => 'admin/structure/entity_example_basic/manage', 'access arguments' => array('administer entity_example_basic entities'), ), ), ), // View modes allow entities to be displayed differently based on context. // As a demonstration we'll support "Tweaky", but we could have and support // multiple display modes. 'view modes' => array( 'tweaky' => array( 'label' => t('Tweaky'), 'custom settings' => FALSE, ), ), ); return $info; } /** * Fetch a basic object. * * This function ends up being a shim between the menu system and * entity_example_basic_load_multiple(). * * This function gets its name from the menu system's wildcard * naming conventions. For example, /path/%wildcard would end * up calling wildcard_load(%wildcard value). In our case defining * the path: examples/entity_example/basic/%entity_example_basic in * hook_menu() tells Drupal to call entity_example_basic_load(). * * @param int $basic_id * Integer specifying the basic entity id. * @param bool $reset * A boolean indicating that the internal cache should be reset. * * @return object * A fully-loaded $basic object or FALSE if it cannot be loaded. * * @see entity_example_basic_load_multiple() * @see entity_example_menu() */ function entity_example_basic_load($basic_id = NULL, $reset = FALSE) { $basic_ids = (isset($basic_id) ? array($basic_id) : array()); $basic = entity_example_basic_load_multiple($basic_ids, array(), $reset); return $basic ? reset($basic) : FALSE; } /** * Loads multiple basic entities. * * We only need to pass this request along to entity_load(), which * will in turn call the load() method of our entity controller class. */ function entity_example_basic_load_multiple($basic_ids = FALSE, $conditions = array(), $reset = FALSE) { return entity_load('entity_example_basic', $basic_ids, $conditions, $reset); } /** * Implements the uri callback. */ function entity_example_basic_uri($basic) { return array( 'path' => 'examples/entity_example/basic/' . $basic->basic_id, ); } /** * Implements hook_menu(). */ function entity_example_menu() { $items['examples/entity_example'] = array( 'title' => 'Entity Example', 'page callback' => 'entity_example_info_page', 'access arguments' => array('view any entity_example_basic entity'), ); // This provides a place for Field API to hang its own // interface and has to be the same as what was defined // in basic_entity_info() above. $items['admin/structure/entity_example_basic/manage'] = array( 'title' => 'Administer entity_example_basic entity type', 'page callback' => 'entity_example_basic_list_entities', 'access arguments' => array('administer entity_example_basic entities'), ); // Add example entities. $items['admin/structure/entity_example_basic/manage/add'] = array( 'title' => 'Add an Entity Example Basic Entity', 'page callback' => 'entity_example_basic_add', 'access arguments' => array('create entity_example_basic entities'), 'type' => MENU_LOCAL_ACTION, ); // List of all entity_example_basic entities. $items['admin/structure/entity_example_basic/manage/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, ); // The page to view our entities - needs to follow what // is defined in basic_uri and will use load_basic to retrieve // the necessary entity info. $items['examples/entity_example/basic/%entity_example_basic'] = array( 'title callback' => 'entity_example_basic_title', 'title arguments' => array(3), 'page callback' => 'entity_example_basic_view', 'page arguments' => array(3), 'access arguments' => array('view any entity_example_basic entity'), ); // 'View' tab for an individual entity page. $items['examples/entity_example/basic/%entity_example_basic/view'] = array( 'title' => 'View', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); // 'Edit' tab for an individual entity page. $items['examples/entity_example/basic/%entity_example_basic/edit'] = array( 'title' => 'Edit', 'page callback' => 'drupal_get_form', 'page arguments' => array('entity_example_basic_form', 3), 'access arguments' => array('edit any entity_example_basic entity'), 'type' => MENU_LOCAL_TASK, ); // Add example entities. $items['examples/entity_example/basic/add'] = array( 'title' => 'Add an Entity Example Basic Entity', 'page callback' => 'entity_example_basic_add', 'access arguments' => array('create entity_example_basic entities'), ); return $items; } /** * Basic information for the page. */ function entity_example_info_page() { $content['preface'] = array( '#type' => 'item', '#markup' => t('The entity example provides a simple example entity.'), ); if (user_access('administer entity_example_basic entities')) { $content['preface']['#markup'] = t('You can administer these and add fields and change the view !link.', array('!link' => l(t('here'), 'admin/structure/entity_example_basic/manage')) ); } $content['table'] = entity_example_basic_list_entities(); return $content; } /** * Implements hook_permission(). */ function entity_example_permission() { $permissions = array( 'administer entity_example_basic entities' => array( 'title' => t('Administer entity_example_basic entities'), ), 'view any entity_example_basic entity' => array( 'title' => t('View any Entity Example Basic entity'), ), 'edit any entity_example_basic entity' => array( 'title' => t('Edit any Entity Example Basic entity'), ), 'create entity_example_basic entities' => array( 'title' => t('Create Entity Example Basic Entities'), ), ); return $permissions; } /** * Returns a render array with all entity_example_basic entities. * * In this basic example we know that there won't be many entities, * so we'll just load them all for display. See pager_example.module * to implement a pager. Most implementations would probably do this * with the contrib Entity API module, or a view using views module, * but we avoid using non-core features in the Examples project. * * @see pager_example.module */ function entity_example_basic_list_entities() { $content = array(); // Load all of our entities. $entities = entity_example_basic_load_multiple(); if (!empty($entities)) { foreach ($entities as $entity) { // Create tabular rows for our entities. $rows[] = array( 'data' => array( 'id' => $entity->basic_id, 'item_description' => l($entity->item_description, 'examples/entity_example/basic/' . $entity->basic_id), 'bundle' => $entity->bundle_type, ), ); } // Put our entities into a themed table. See theme_table() for details. $content['entity_table'] = array( '#theme' => 'table', '#rows' => $rows, '#header' => array(t('ID'), t('Item Description'), t('Bundle')), ); } else { // There were no entities. Tell the user. $content[] = array( '#type' => 'item', '#markup' => t('No entity_example_basic entities currently exist.'), ); } return $content; } /** * Callback for a page title when this entity is displayed. */ function entity_example_basic_title($entity) { return t('Entity Example Basic (item_description=@item_description)', array('@item_description' => $entity->item_description)); } /** * Menu callback to display an entity. * * As we load the entity for display, we're responsible for invoking a number * of hooks in their proper order. * * @see hook_entity_prepare_view() * @see hook_entity_view() * @see hook_entity_view_alter() */ function entity_example_basic_view($entity, $view_mode = 'tweaky') { // Our entity type, for convenience. $entity_type = 'entity_example_basic'; // Start setting up the content. $entity->content = array( '#view_mode' => $view_mode, ); // Build fields content - this is where the Field API really comes in to play. // The task has very little code here because it all gets taken care of by // field module. // field_attach_prepare_view() lets the fields load any data they need // before viewing. field_attach_prepare_view($entity_type, array($entity->basic_id => $entity), $view_mode); // We call entity_prepare_view() so it can invoke hook_entity_prepare_view() // for us. entity_prepare_view($entity_type, array($entity->basic_id => $entity)); // Now field_attach_view() generates the content for the fields. $entity->content += field_attach_view($entity_type, $entity, $view_mode); // OK, Field API done, now we can set up some of our own data. $entity->content['created'] = array( '#type' => 'item', '#title' => t('Created date'), '#markup' => format_date($entity->created), ); $entity->content['item_description'] = array( '#type' => 'item', '#title' => t('Item Description'), '#markup' => $entity->item_description, ); // Now to invoke some hooks. We need the language code for // hook_entity_view(), so let's get that. global $language; $langcode = $language->language; // And now invoke hook_entity_view(). module_invoke_all('entity_view', $entity, $entity_type, $view_mode, $langcode); // Now invoke hook_entity_view_alter(). drupal_alter(array('entity_example_basic_view', 'entity_view'), $entity->content, $entity_type); // And finally return the content. return $entity->content; } /** * Implements hook_field_extra_fields(). * * This exposes the "extra fields" (usually properties that can be configured * as if they were fields) of the entity as pseudo-fields * so that they get handled by the Entity and Field core functionality. * Node titles get treated in a similar manner. */ function entity_example_field_extra_fields() { $form_elements['item_description'] = array( 'label' => t('Item Description'), 'description' => t('Item Description (an extra form field)'), 'weight' => -5, ); $display_elements['created'] = array( 'label' => t('Creation date'), 'description' => t('Creation date (an extra display field)'), 'weight' => 0, ); $display_elements['item_description'] = array( 'label' => t('Item Description'), 'description' => t('Just like title, but trying to point out that it is a separate property'), 'weight' => 0, ); // Since we have only one bundle type, we'll just provide the extra_fields // for it here. $extra_fields['entity_example_basic']['first_example_bundle']['form'] = $form_elements; $extra_fields['entity_example_basic']['first_example_bundle']['display'] = $display_elements; return $extra_fields; } /** * Provides a wrapper on the edit form to add a new entity. */ function entity_example_basic_add() { // Create a basic entity structure to be used and passed to the validation // and submission functions. $entity = entity_get_controller('entity_example_basic')->create(); return drupal_get_form('entity_example_basic_form', $entity); } /** * Form function to create an entity_example_basic entity. * * The pattern is: * - Set up the form for the data that is specific to your * entity: the columns of your base table. * - Call on the Field API to pull in the form elements * for fields attached to the entity. */ function entity_example_basic_form($form, &$form_state, $entity) { $form['item_description'] = array( '#type' => 'textfield', '#title' => t('Item Description'), '#required' => TRUE, '#default_value' => $entity->item_description, ); $form['basic_entity'] = array( '#type' => 'value', '#value' => $entity, ); field_attach_form('entity_example_basic', $entity, $form, $form_state); $form['submit'] = array( '#type' => 'submit', '#value' => t('Save'), '#weight' => 100, ); $form['delete'] = array( '#type' => 'submit', '#value' => t('Delete'), '#submit' => array('entity_example_basic_edit_delete'), '#weight' => 200, ); return $form; } /** * Validation handler for entity_example_basic_add_form form. * * We pass things straight through to the Field API to handle validation * of the attached fields. */ function entity_example_basic_form_validate($form, &$form_state) { field_attach_form_validate('entity_example_basic', $form_state['values']['basic_entity'], $form, $form_state); } /** * Form submit handler: Submits basic_add_form information. */ function entity_example_basic_form_submit($form, &$form_state) { $entity = $form_state['values']['basic_entity']; $entity->item_description = $form_state['values']['item_description']; field_attach_submit('entity_example_basic', $entity, $form, $form_state); $entity = entity_example_basic_save($entity); $form_state['redirect'] = 'examples/entity_example/basic/' . $entity->basic_id; } /** * Form deletion handler. * * @todo: 'Are you sure?' message. */ function entity_example_basic_edit_delete($form, &$form_state) { $entity = $form_state['values']['basic_entity']; entity_example_basic_delete($entity); drupal_set_message(t('The entity %item_description (ID %id) has been deleted', array('%item_description' => $entity->item_description, '%id' => $entity->basic_id)) ); $form_state['redirect'] = 'examples/entity_example'; } /** * We save the entity by calling the controller. */ function entity_example_basic_save(&$entity) { return entity_get_controller('entity_example_basic')->save($entity); } /** * Use the controller to delete the entity. */ function entity_example_basic_delete($entity) { entity_get_controller('entity_example_basic')->delete($entity); } /** * EntityExampleBasicControllerInterface definition. * * We create an interface here because anyone could come along and * use hook_entity_info_alter() to change our controller class. * We want to let them know what methods our class needs in order * to function with the rest of the module, so here's a handy list. * * @see hook_entity_info_alter() */ interface EntityExampleBasicControllerInterface extends DrupalEntityControllerInterface { /** * Create an entity. */ public function create(); /** * Save an entity. * * @param object $entity * The entity to save. */ public function save($entity); /** * Delete an entity. * * @param object $entity * The entity to delete. */ public function delete($entity); } /** * EntityExampleBasicController extends DrupalDefaultEntityController. * * Our subclass of DrupalDefaultEntityController lets us add a few * important create, update, and delete methods. */ class EntityExampleBasicController extends DrupalDefaultEntityController implements EntityExampleBasicControllerInterface { /** * Create and return a new entity_example_basic entity. */ public function create() { $entity = new stdClass(); $entity->type = 'entity_example_basic'; $entity->basic_id = 0; $entity->bundle_type = 'first_example_bundle'; $entity->item_description = ''; return $entity; } /** * Saves the custom fields using drupal_write_record(). */ public function save($entity) { // If our entity has no basic_id, then we need to give it a // time of creation. if (empty($entity->basic_id)) { $entity->created = time(); } // Invoke hook_entity_presave(). module_invoke_all('entity_presave', $entity, 'entity_example_basic'); // The 'primary_keys' argument determines whether this will be an insert // or an update. So if the entity already has an ID, we'll specify // basic_id as the key. $primary_keys = $entity->basic_id ? 'basic_id' : array(); // Write out the entity record. drupal_write_record('entity_example_basic', $entity, $primary_keys); // We're going to invoke either hook_entity_update() or // hook_entity_insert(), depending on whether or not this is a // new entity. We'll just store the name of hook_entity_insert() // and change it if we need to. $invocation = 'entity_insert'; // Now we need to either insert or update the fields which are // attached to this entity. We use the same primary_keys logic // to determine whether to update or insert, and which hook we // need to invoke. if (empty($primary_keys)) { field_attach_insert('entity_example_basic', $entity); } else { field_attach_update('entity_example_basic', $entity); $invocation = 'entity_update'; } // Invoke either hook_entity_update() or hook_entity_insert(). module_invoke_all($invocation, $entity, 'entity_example_basic'); return $entity; } /** * Delete a single entity. * * Really a convenience function for deleteMultiple(). */ public function delete($entity) { $this->deleteMultiple(array($entity)); } /** * Delete one or more entity_example_basic entities. * * Deletion is unfortunately not supported in the base * DrupalDefaultEntityController class. * * @param array $entities * An array of entity IDs or a single numeric ID. */ public function deleteMultiple($entities) { $basic_ids = array(); if (!empty($entities)) { $transaction = db_transaction(); try { foreach ($entities as $entity) { // Invoke hook_entity_delete(). module_invoke_all('entity_delete', $entity, 'entity_example_basic'); field_attach_delete('entity_example_basic', $entity); $basic_ids[] = $entity->basic_id; } db_delete('entity_example_basic') ->condition('basic_id', $basic_ids, 'IN') ->execute(); } catch (Exception $e) { $transaction->rollback(); watchdog_exception('entity_example', $e); throw $e; } } } } /** * @} End of "defgroup entity_example". */