entity_example.module 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. <?php
  2. /**
  3. * @file
  4. * Implements the basic functionality required to create and display an entity.
  5. *
  6. * This example does not use the
  7. * @link http://drupal.org/project/entity Entity API module @endlink, which is
  8. * used by many entity implementations and is recommended by many.
  9. *
  10. * An example of use of creating and managing entities using the Entity API
  11. * module is provided in the
  12. * @link http://drupal.org/project/model Model Entity module @endlink.
  13. *
  14. * @todo: Reference the ronald_istos article series
  15. * @todo: Reference the Drupal module development book
  16. * @todo: Add a single field
  17. */
  18. /**
  19. * @defgroup entity_example Example: Entity
  20. * @ingroup examples
  21. * @{
  22. * Example creating a core Entity API entity.
  23. *
  24. * Note that this example does not use or demonstrate the contrib Entity API,
  25. * which you can find here: http://drupal.org/project/entity
  26. */
  27. /**
  28. * Implements hook_entity_info().
  29. *
  30. * This is the fundamental description of the entity.
  31. *
  32. * It provides a single entity with a single bundle and without revision
  33. * support.
  34. */
  35. function entity_example_entity_info() {
  36. $info['entity_example_basic'] = array(
  37. // A human readable label to identify our entity.
  38. 'label' => t('Example Basic Entity'),
  39. // The controller for our Entity, extending the Drupal core controller.
  40. 'controller class' => 'EntityExampleBasicController',
  41. // The table for this entity defined in hook_schema()
  42. 'base table' => 'entity_example_basic',
  43. // Returns the uri elements of an entity.
  44. 'uri callback' => 'entity_example_basic_uri',
  45. // IF fieldable == FALSE, we can't attach fields.
  46. 'fieldable' => TRUE,
  47. // entity_keys tells the controller what database fields are used for key
  48. // functions. It is not required if we don't have bundles or revisions.
  49. // Here we do not support a revision, so that entity key is omitted.
  50. 'entity keys' => array(
  51. // The 'id' (basic_id here) is the unique id.
  52. 'id' => 'basic_id' ,
  53. // Bundle will be determined by the 'bundle_type' field.
  54. 'bundle' => 'bundle_type',
  55. ),
  56. 'bundle keys' => array(
  57. 'bundle' => 'bundle_type',
  58. ),
  59. // FALSE disables caching. Caching functionality is handled by Drupal core.
  60. 'static cache' => TRUE,
  61. // Bundles are alternative groups of fields or configuration
  62. // associated with a base entity type.
  63. 'bundles' => array(
  64. 'first_example_bundle' => array(
  65. 'label' => 'First example bundle',
  66. // 'admin' key is used by the Field UI to provide field and
  67. // display UI pages.
  68. 'admin' => array(
  69. 'path' => 'admin/structure/entity_example_basic/manage',
  70. 'access arguments' => array('administer entity_example_basic entities'),
  71. ),
  72. ),
  73. ),
  74. // View modes allow entities to be displayed differently based on context.
  75. // As a demonstration we'll support "Tweaky", but we could have and support
  76. // multiple display modes.
  77. 'view modes' => array(
  78. 'tweaky' => array(
  79. 'label' => t('Tweaky'),
  80. 'custom settings' => FALSE,
  81. ),
  82. ),
  83. );
  84. return $info;
  85. }
  86. /**
  87. * Fetch a basic object.
  88. *
  89. * This function ends up being a shim between the menu system and
  90. * entity_example_basic_load_multiple().
  91. *
  92. * This function gets its name from the menu system's wildcard
  93. * naming conventions. For example, /path/%wildcard would end
  94. * up calling wildcard_load(%wildcard value). In our case defining
  95. * the path: examples/entity_example/basic/%entity_example_basic in
  96. * hook_menu() tells Drupal to call entity_example_basic_load().
  97. *
  98. * @param int $basic_id
  99. * Integer specifying the basic entity id.
  100. * @param bool $reset
  101. * A boolean indicating that the internal cache should be reset.
  102. *
  103. * @return object
  104. * A fully-loaded $basic object or FALSE if it cannot be loaded.
  105. *
  106. * @see entity_example_basic_load_multiple()
  107. * @see entity_example_menu()
  108. */
  109. function entity_example_basic_load($basic_id = NULL, $reset = FALSE) {
  110. $basic_ids = (isset($basic_id) ? array($basic_id) : array());
  111. $basic = entity_example_basic_load_multiple($basic_ids, array(), $reset);
  112. return $basic ? reset($basic) : FALSE;
  113. }
  114. /**
  115. * Loads multiple basic entities.
  116. *
  117. * We only need to pass this request along to entity_load(), which
  118. * will in turn call the load() method of our entity controller class.
  119. */
  120. function entity_example_basic_load_multiple($basic_ids = FALSE, $conditions = array(), $reset = FALSE) {
  121. return entity_load('entity_example_basic', $basic_ids, $conditions, $reset);
  122. }
  123. /**
  124. * Implements the uri callback.
  125. */
  126. function entity_example_basic_uri($basic) {
  127. return array(
  128. 'path' => 'examples/entity_example/basic/' . $basic->basic_id,
  129. );
  130. }
  131. /**
  132. * Implements hook_menu().
  133. */
  134. function entity_example_menu() {
  135. $items['examples/entity_example'] = array(
  136. 'title' => 'Entity Example',
  137. 'page callback' => 'entity_example_info_page',
  138. 'access arguments' => array('view any entity_example_basic entity'),
  139. );
  140. // This provides a place for Field API to hang its own
  141. // interface and has to be the same as what was defined
  142. // in basic_entity_info() above.
  143. $items['admin/structure/entity_example_basic/manage'] = array(
  144. 'title' => 'Administer entity_example_basic entity type',
  145. 'page callback' => 'entity_example_basic_list_entities',
  146. 'access arguments' => array('administer entity_example_basic entities'),
  147. );
  148. // Add example entities.
  149. $items['admin/structure/entity_example_basic/manage/add'] = array(
  150. 'title' => 'Add an Entity Example Basic Entity',
  151. 'page callback' => 'entity_example_basic_add',
  152. 'access arguments' => array('create entity_example_basic entities'),
  153. 'type' => MENU_LOCAL_ACTION,
  154. );
  155. // List of all entity_example_basic entities.
  156. $items['admin/structure/entity_example_basic/manage/list'] = array(
  157. 'title' => 'List',
  158. 'type' => MENU_DEFAULT_LOCAL_TASK,
  159. );
  160. // The page to view our entities - needs to follow what
  161. // is defined in basic_uri and will use load_basic to retrieve
  162. // the necessary entity info.
  163. $items['examples/entity_example/basic/%entity_example_basic'] = array(
  164. 'title callback' => 'entity_example_basic_title',
  165. 'title arguments' => array(3),
  166. 'page callback' => 'entity_example_basic_view',
  167. 'page arguments' => array(3),
  168. 'access arguments' => array('view any entity_example_basic entity'),
  169. );
  170. // 'View' tab for an individual entity page.
  171. $items['examples/entity_example/basic/%entity_example_basic/view'] = array(
  172. 'title' => 'View',
  173. 'type' => MENU_DEFAULT_LOCAL_TASK,
  174. 'weight' => -10,
  175. );
  176. // 'Edit' tab for an individual entity page.
  177. $items['examples/entity_example/basic/%entity_example_basic/edit'] = array(
  178. 'title' => 'Edit',
  179. 'page callback' => 'drupal_get_form',
  180. 'page arguments' => array('entity_example_basic_form', 3),
  181. 'access arguments' => array('edit any entity_example_basic entity'),
  182. 'type' => MENU_LOCAL_TASK,
  183. );
  184. // Add example entities.
  185. $items['examples/entity_example/basic/add'] = array(
  186. 'title' => 'Add an Entity Example Basic Entity',
  187. 'page callback' => 'entity_example_basic_add',
  188. 'access arguments' => array('create entity_example_basic entities'),
  189. );
  190. return $items;
  191. }
  192. /**
  193. * Basic information for the page.
  194. */
  195. function entity_example_info_page() {
  196. $content['preface'] = array(
  197. '#type' => 'item',
  198. '#markup' => t('The entity example provides a simple example entity.'),
  199. );
  200. if (user_access('administer entity_example_basic entities')) {
  201. $content['preface']['#markup'] = t('You can administer these and add fields and change the view !link.',
  202. array('!link' => l(t('here'), 'admin/structure/entity_example_basic/manage'))
  203. );
  204. }
  205. $content['table'] = entity_example_basic_list_entities();
  206. return $content;
  207. }
  208. /**
  209. * Implements hook_permission().
  210. */
  211. function entity_example_permission() {
  212. $permissions = array(
  213. 'administer entity_example_basic entities' => array(
  214. 'title' => t('Administer entity_example_basic entities'),
  215. ),
  216. 'view any entity_example_basic entity' => array(
  217. 'title' => t('View any Entity Example Basic entity'),
  218. ),
  219. 'edit any entity_example_basic entity' => array(
  220. 'title' => t('Edit any Entity Example Basic entity'),
  221. ),
  222. 'create entity_example_basic entities' => array(
  223. 'title' => t('Create Entity Example Basic Entities'),
  224. ),
  225. );
  226. return $permissions;
  227. }
  228. /**
  229. * Returns a render array with all entity_example_basic entities.
  230. *
  231. * In this basic example we know that there won't be many entities,
  232. * so we'll just load them all for display. See pager_example.module
  233. * to implement a pager. Most implementations would probably do this
  234. * with the contrib Entity API module, or a view using views module,
  235. * but we avoid using non-core features in the Examples project.
  236. *
  237. * @see pager_example.module
  238. */
  239. function entity_example_basic_list_entities() {
  240. $content = array();
  241. // Load all of our entities.
  242. $entities = entity_example_basic_load_multiple();
  243. if (!empty($entities)) {
  244. foreach ($entities as $entity) {
  245. // Create tabular rows for our entities.
  246. $rows[] = array(
  247. 'data' => array(
  248. 'id' => $entity->basic_id,
  249. 'item_description' => l($entity->item_description, 'examples/entity_example/basic/' . $entity->basic_id),
  250. 'bundle' => $entity->bundle_type,
  251. ),
  252. );
  253. }
  254. // Put our entities into a themed table. See theme_table() for details.
  255. $content['entity_table'] = array(
  256. '#theme' => 'table',
  257. '#rows' => $rows,
  258. '#header' => array(t('ID'), t('Item Description'), t('Bundle')),
  259. );
  260. }
  261. else {
  262. // There were no entities. Tell the user.
  263. $content[] = array(
  264. '#type' => 'item',
  265. '#markup' => t('No entity_example_basic entities currently exist.'),
  266. );
  267. }
  268. return $content;
  269. }
  270. /**
  271. * Callback for a page title when this entity is displayed.
  272. */
  273. function entity_example_basic_title($entity) {
  274. return t('Entity Example Basic (item_description=@item_description)', array('@item_description' => $entity->item_description));
  275. }
  276. /**
  277. * Menu callback to display an entity.
  278. *
  279. * As we load the entity for display, we're responsible for invoking a number
  280. * of hooks in their proper order.
  281. *
  282. * @see hook_entity_prepare_view()
  283. * @see hook_entity_view()
  284. * @see hook_entity_view_alter()
  285. */
  286. function entity_example_basic_view($entity, $view_mode = 'tweaky') {
  287. // Our entity type, for convenience.
  288. $entity_type = 'entity_example_basic';
  289. // Start setting up the content.
  290. $entity->content = array(
  291. '#view_mode' => $view_mode,
  292. );
  293. // Build fields content - this is where the Field API really comes in to play.
  294. // The task has very little code here because it all gets taken care of by
  295. // field module.
  296. // field_attach_prepare_view() lets the fields load any data they need
  297. // before viewing.
  298. field_attach_prepare_view($entity_type, array($entity->basic_id => $entity),
  299. $view_mode);
  300. // We call entity_prepare_view() so it can invoke hook_entity_prepare_view()
  301. // for us.
  302. entity_prepare_view($entity_type, array($entity->basic_id => $entity));
  303. // Now field_attach_view() generates the content for the fields.
  304. $entity->content += field_attach_view($entity_type, $entity, $view_mode);
  305. // OK, Field API done, now we can set up some of our own data.
  306. $entity->content['created'] = array(
  307. '#type' => 'item',
  308. '#title' => t('Created date'),
  309. '#markup' => format_date($entity->created),
  310. );
  311. $entity->content['item_description'] = array(
  312. '#type' => 'item',
  313. '#title' => t('Item Description'),
  314. '#markup' => $entity->item_description,
  315. );
  316. // Now to invoke some hooks. We need the language code for
  317. // hook_entity_view(), so let's get that.
  318. global $language;
  319. $langcode = $language->language;
  320. // And now invoke hook_entity_view().
  321. module_invoke_all('entity_view', $entity, $entity_type, $view_mode,
  322. $langcode);
  323. // Now invoke hook_entity_view_alter().
  324. drupal_alter(array('entity_example_basic_view', 'entity_view'),
  325. $entity->content, $entity_type);
  326. // And finally return the content.
  327. return $entity->content;
  328. }
  329. /**
  330. * Implements hook_field_extra_fields().
  331. *
  332. * This exposes the "extra fields" (usually properties that can be configured
  333. * as if they were fields) of the entity as pseudo-fields
  334. * so that they get handled by the Entity and Field core functionality.
  335. * Node titles get treated in a similar manner.
  336. */
  337. function entity_example_field_extra_fields() {
  338. $form_elements['item_description'] = array(
  339. 'label' => t('Item Description'),
  340. 'description' => t('Item Description (an extra form field)'),
  341. 'weight' => -5,
  342. );
  343. $display_elements['created'] = array(
  344. 'label' => t('Creation date'),
  345. 'description' => t('Creation date (an extra display field)'),
  346. 'weight' => 0,
  347. );
  348. $display_elements['item_description'] = array(
  349. 'label' => t('Item Description'),
  350. 'description' => t('Just like title, but trying to point out that it is a separate property'),
  351. 'weight' => 0,
  352. );
  353. // Since we have only one bundle type, we'll just provide the extra_fields
  354. // for it here.
  355. $extra_fields['entity_example_basic']['first_example_bundle']['form'] = $form_elements;
  356. $extra_fields['entity_example_basic']['first_example_bundle']['display'] = $display_elements;
  357. return $extra_fields;
  358. }
  359. /**
  360. * Provides a wrapper on the edit form to add a new entity.
  361. */
  362. function entity_example_basic_add() {
  363. // Create a basic entity structure to be used and passed to the validation
  364. // and submission functions.
  365. $entity = entity_get_controller('entity_example_basic')->create();
  366. return drupal_get_form('entity_example_basic_form', $entity);
  367. }
  368. /**
  369. * Form function to create an entity_example_basic entity.
  370. *
  371. * The pattern is:
  372. * - Set up the form for the data that is specific to your
  373. * entity: the columns of your base table.
  374. * - Call on the Field API to pull in the form elements
  375. * for fields attached to the entity.
  376. */
  377. function entity_example_basic_form($form, &$form_state, $entity) {
  378. $form['item_description'] = array(
  379. '#type' => 'textfield',
  380. '#title' => t('Item Description'),
  381. '#required' => TRUE,
  382. '#default_value' => $entity->item_description,
  383. );
  384. $form['basic_entity'] = array(
  385. '#type' => 'value',
  386. '#value' => $entity,
  387. );
  388. field_attach_form('entity_example_basic', $entity, $form, $form_state);
  389. $form['submit'] = array(
  390. '#type' => 'submit',
  391. '#value' => t('Save'),
  392. '#weight' => 100,
  393. );
  394. $form['delete'] = array(
  395. '#type' => 'submit',
  396. '#value' => t('Delete'),
  397. '#submit' => array('entity_example_basic_edit_delete'),
  398. '#weight' => 200,
  399. );
  400. return $form;
  401. }
  402. /**
  403. * Validation handler for entity_example_basic_add_form form.
  404. *
  405. * We pass things straight through to the Field API to handle validation
  406. * of the attached fields.
  407. */
  408. function entity_example_basic_form_validate($form, &$form_state) {
  409. field_attach_form_validate('entity_example_basic', $form_state['values']['basic_entity'], $form, $form_state);
  410. }
  411. /**
  412. * Form submit handler: Submits basic_add_form information.
  413. */
  414. function entity_example_basic_form_submit($form, &$form_state) {
  415. $entity = $form_state['values']['basic_entity'];
  416. $entity->item_description = $form_state['values']['item_description'];
  417. field_attach_submit('entity_example_basic', $entity, $form, $form_state);
  418. $entity = entity_example_basic_save($entity);
  419. $form_state['redirect'] = 'examples/entity_example/basic/' . $entity->basic_id;
  420. }
  421. /**
  422. * Form deletion handler.
  423. *
  424. * @todo: 'Are you sure?' message.
  425. */
  426. function entity_example_basic_edit_delete($form, &$form_state) {
  427. $entity = $form_state['values']['basic_entity'];
  428. entity_example_basic_delete($entity);
  429. drupal_set_message(t('The entity %item_description (ID %id) has been deleted',
  430. array('%item_description' => $entity->item_description, '%id' => $entity->basic_id))
  431. );
  432. $form_state['redirect'] = 'examples/entity_example';
  433. }
  434. /**
  435. * We save the entity by calling the controller.
  436. */
  437. function entity_example_basic_save(&$entity) {
  438. return entity_get_controller('entity_example_basic')->save($entity);
  439. }
  440. /**
  441. * Use the controller to delete the entity.
  442. */
  443. function entity_example_basic_delete($entity) {
  444. entity_get_controller('entity_example_basic')->delete($entity);
  445. }
  446. /**
  447. * EntityExampleBasicControllerInterface definition.
  448. *
  449. * We create an interface here because anyone could come along and
  450. * use hook_entity_info_alter() to change our controller class.
  451. * We want to let them know what methods our class needs in order
  452. * to function with the rest of the module, so here's a handy list.
  453. *
  454. * @see hook_entity_info_alter()
  455. */
  456. interface EntityExampleBasicControllerInterface
  457. extends DrupalEntityControllerInterface {
  458. /**
  459. * Create an entity.
  460. */
  461. public function create();
  462. /**
  463. * Save an entity.
  464. *
  465. * @param object $entity
  466. * The entity to save.
  467. */
  468. public function save($entity);
  469. /**
  470. * Delete an entity.
  471. *
  472. * @param object $entity
  473. * The entity to delete.
  474. */
  475. public function delete($entity);
  476. }
  477. /**
  478. * EntityExampleBasicController extends DrupalDefaultEntityController.
  479. *
  480. * Our subclass of DrupalDefaultEntityController lets us add a few
  481. * important create, update, and delete methods.
  482. */
  483. class EntityExampleBasicController
  484. extends DrupalDefaultEntityController
  485. implements EntityExampleBasicControllerInterface {
  486. /**
  487. * Create and return a new entity_example_basic entity.
  488. */
  489. public function create() {
  490. $entity = new stdClass();
  491. $entity->type = 'entity_example_basic';
  492. $entity->basic_id = 0;
  493. $entity->bundle_type = 'first_example_bundle';
  494. $entity->item_description = '';
  495. return $entity;
  496. }
  497. /**
  498. * Saves the custom fields using drupal_write_record().
  499. */
  500. public function save($entity) {
  501. // If our entity has no basic_id, then we need to give it a
  502. // time of creation.
  503. if (empty($entity->basic_id)) {
  504. $entity->created = time();
  505. }
  506. // Invoke hook_entity_presave().
  507. module_invoke_all('entity_presave', $entity, 'entity_example_basic');
  508. // The 'primary_keys' argument determines whether this will be an insert
  509. // or an update. So if the entity already has an ID, we'll specify
  510. // basic_id as the key.
  511. $primary_keys = $entity->basic_id ? 'basic_id' : array();
  512. // Write out the entity record.
  513. drupal_write_record('entity_example_basic', $entity, $primary_keys);
  514. // We're going to invoke either hook_entity_update() or
  515. // hook_entity_insert(), depending on whether or not this is a
  516. // new entity. We'll just store the name of hook_entity_insert()
  517. // and change it if we need to.
  518. $invocation = 'entity_insert';
  519. // Now we need to either insert or update the fields which are
  520. // attached to this entity. We use the same primary_keys logic
  521. // to determine whether to update or insert, and which hook we
  522. // need to invoke.
  523. if (empty($primary_keys)) {
  524. field_attach_insert('entity_example_basic', $entity);
  525. }
  526. else {
  527. field_attach_update('entity_example_basic', $entity);
  528. $invocation = 'entity_update';
  529. }
  530. // Invoke either hook_entity_update() or hook_entity_insert().
  531. module_invoke_all($invocation, $entity, 'entity_example_basic');
  532. return $entity;
  533. }
  534. /**
  535. * Delete a single entity.
  536. *
  537. * Really a convenience function for deleteMultiple().
  538. */
  539. public function delete($entity) {
  540. $this->deleteMultiple(array($entity));
  541. }
  542. /**
  543. * Delete one or more entity_example_basic entities.
  544. *
  545. * Deletion is unfortunately not supported in the base
  546. * DrupalDefaultEntityController class.
  547. *
  548. * @param array $entities
  549. * An array of entity IDs or a single numeric ID.
  550. */
  551. public function deleteMultiple($entities) {
  552. $basic_ids = array();
  553. if (!empty($entities)) {
  554. $transaction = db_transaction();
  555. try {
  556. foreach ($entities as $entity) {
  557. // Invoke hook_entity_delete().
  558. module_invoke_all('entity_delete', $entity, 'entity_example_basic');
  559. field_attach_delete('entity_example_basic', $entity);
  560. $basic_ids[] = $entity->basic_id;
  561. }
  562. db_delete('entity_example_basic')
  563. ->condition('basic_id', $basic_ids, 'IN')
  564. ->execute();
  565. }
  566. catch (Exception $e) {
  567. $transaction->rollback();
  568. watchdog_exception('entity_example', $e);
  569. throw $e;
  570. }
  571. }
  572. }
  573. }
  574. /**
  575. * @} End of "defgroup entity_example".
  576. */