dbtng_example.module 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. <?php
  2. /**
  3. * @file
  4. * This is an example outlining how a module can make use of the new DBTNG
  5. * database API in Drupal 7.
  6. *
  7. * @todo Demonstrate transaction usage.
  8. *
  9. * General documentation is available at
  10. * @link database Database abstraction layer documentation @endlink and
  11. * at @link http://drupal.org/node/310069 @endlink.
  12. */
  13. /**
  14. * @defgroup dbtng_example Example: Database (DBTNG)
  15. * @ingroup examples
  16. * @{
  17. * Database examples, including DBTNG.
  18. *
  19. * 'DBTNG' means 'Database: The Next Generation.' Yes, Drupallers are nerds.
  20. *
  21. * General documentation is available at
  22. * @link database.inc database abstraction layer documentation @endlink and
  23. * at @link http://drupal.org/node/310069 Database API @endlink.
  24. *
  25. * The several examples here demonstrate basic database usage.
  26. *
  27. * In Drupal 6, the recommended method to save or update an entry in the
  28. * database was drupal_write_record() or db_query().
  29. *
  30. * In Drupal 7 and forward, the usage of db_query()
  31. * for INSERT, UPDATE, or DELETE is deprecated, because it is
  32. * database-dependent. Instead specific functions are provided to perform these
  33. * operations: db_insert(), db_update(), and db_delete() do the job now.
  34. * (Note that drupal_write_record() is also deprecated.)
  35. *
  36. * db_insert() example:
  37. * @code
  38. * // INSERT INTO {dbtng_example} (name, surname) VALUES('John, 'Doe')
  39. * db_insert('dbtng_example')
  40. * ->fields(array('name' => 'John', 'surname' => 'Doe'))
  41. * ->execute();
  42. * @endcode
  43. *
  44. * db_update() example:
  45. * @code
  46. * // UPDATE {dbtng_example} SET name = 'Jane' WHERE name = 'John'
  47. * db_update('dbtng_example')
  48. * ->fields(array('name' => 'Jane'))
  49. * ->condition('name', 'John')
  50. * ->execute();
  51. * @endcode
  52. *
  53. * db_delete() example:
  54. * @code
  55. * // DELETE FROM {dbtng_example} WHERE name = 'Jane'
  56. * db_delete('dbtng_example')
  57. * ->condition('name', 'Jane')
  58. * ->execute();
  59. * @endcode
  60. *
  61. * See @link database Database Abstraction Layer @endlink
  62. * @see db_insert()
  63. * @see db_update()
  64. * @see db_delete()
  65. * @see drupal_write_record()
  66. */
  67. /**
  68. * Save an entry in the database.
  69. *
  70. * The underlying DBTNG function is db_insert().
  71. *
  72. * In Drupal 6, this would have been:
  73. * @code
  74. * db_query(
  75. * "INSERT INTO {dbtng_example} (name, surname, age)
  76. * VALUES ('%s', '%s', '%d')",
  77. * $entry['name'],
  78. * $entry['surname'],
  79. * $entry['age']
  80. * );
  81. * @endcode
  82. *
  83. * Exception handling is shown in this example. It could be simplified
  84. * without the try/catch blocks, but since an insert will throw an exception
  85. * and terminate your application if the exception is not handled, it is best
  86. * to employ try/catch.
  87. *
  88. * @param array $entry
  89. * An array containing all the fields of the database record.
  90. *
  91. * @see db_insert()
  92. */
  93. function dbtng_example_entry_insert($entry) {
  94. $return_value = NULL;
  95. try {
  96. $return_value = db_insert('dbtng_example')
  97. ->fields($entry)
  98. ->execute();
  99. }
  100. catch (Exception $e) {
  101. drupal_set_message(t('db_insert failed. Message = %message, query= %query',
  102. array('%message' => $e->getMessage(), '%query' => $e->query_string)), 'error');
  103. }
  104. return $return_value;
  105. }
  106. /**
  107. * Update an entry in the database.
  108. *
  109. * The former, deprecated techniques used db_query() or drupal_write_record():
  110. * @code
  111. * drupal_write_record('dbtng_example', $entry, $entry['pid']);
  112. * @endcode
  113. *
  114. * @code
  115. * db_query(
  116. * "UPDATE {dbtng_example}
  117. * SET name = '%s', surname = '%s', age = '%d'
  118. * WHERE pid = %d",
  119. * $entry['pid']
  120. * );
  121. * @endcode
  122. *
  123. * @param array $entry
  124. * An array containing all the fields of the item to be updated.
  125. *
  126. * @see db_update()
  127. */
  128. function dbtng_example_entry_update($entry) {
  129. try {
  130. // db_update()...->execute() returns the number of rows updated.
  131. $count = db_update('dbtng_example')
  132. ->fields($entry)
  133. ->condition('pid', $entry['pid'])
  134. ->execute();
  135. }
  136. catch (Exception $e) {
  137. drupal_set_message(t('db_update failed. Message = %message, query= %query',
  138. array('%message' => $e->getMessage(), '%query' => $e->query_string)), 'error');
  139. }
  140. return $count;
  141. }
  142. /**
  143. * Delete an entry from the database.
  144. *
  145. * The usage of db_query is deprecated except for static queries.
  146. * Formerly, a deletion might have been accomplished like this:
  147. * @code
  148. * db_query("DELETE FROM {dbtng_example} WHERE pid = %d", $entry['pid]);
  149. * @endcode
  150. *
  151. * @param array $entry
  152. * An array containing at least the person identifier 'pid' element of the
  153. * entry to delete.
  154. *
  155. * @see db_delete()
  156. */
  157. function dbtng_example_entry_delete($entry) {
  158. db_delete('dbtng_example')
  159. ->condition('pid', $entry['pid'])
  160. ->execute();
  161. }
  162. /**
  163. * Read from the database using a filter array.
  164. *
  165. * In Drupal 6, the standard function to perform reads was db_query(), and
  166. * for static queries, it still is.
  167. *
  168. * db_query() used an SQL query with placeholders and arguments as parameters.
  169. *
  170. * @code
  171. * // Old way
  172. * $query = "SELECT * FROM {dbtng_example} n WHERE n.uid = %d AND name = '%s'";
  173. * $result = db_query($query, $uid, $name);
  174. * @endcode
  175. *
  176. * Drupal 7 DBTNG provides an abstracted interface that will work with a wide
  177. * variety of database engines.
  178. *
  179. * db_query() is deprecated except when doing a static query. The following is
  180. * perfectly acceptable in Drupal 7. See
  181. * @link http://drupal.org/node/310072 the handbook page on static queries @endlink
  182. *
  183. * @code
  184. * // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
  185. * db_query(
  186. * "SELECT * FROM {dbtng_example} WHERE uid = :uid and name = :name",
  187. * array(':uid' => 0, ':name' => 'John')
  188. * )->execute();
  189. * @endcode
  190. *
  191. * But for more dynamic queries, Drupal provides the db_select() API method, so
  192. * there are several ways to perform the same SQL query. See the
  193. * @link http://drupal.org/node/310075 handbook page on dynamic queries. @endlink
  194. *
  195. * @code
  196. * // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
  197. * db_select('dbtng_example')
  198. * ->fields('dbtng_example')
  199. * ->condition('uid', 0)
  200. * ->condition('name', 'John')
  201. * ->execute();
  202. * @endcode
  203. *
  204. * Here is db_select with named placeholders:
  205. * @code
  206. * // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
  207. * $arguments = array(':name' => 'John', ':uid' => 0);
  208. * db_select('dbtng_example')
  209. * ->fields('dbtng_example')
  210. * ->where('uid = :uid AND name = :name', $arguments)
  211. * ->execute();
  212. * @endcode
  213. *
  214. * Conditions are stacked and evaluated as AND and OR depending on the type of
  215. * query. For more information, read the conditional queries handbook page at:
  216. * http://drupal.org/node/310086
  217. *
  218. * The condition argument is an 'equal' evaluation by default, but this can be
  219. * altered:
  220. * @code
  221. * // SELECT * FROM {dbtng_example} WHERE age > 18
  222. * db_select('dbtng_example')
  223. * ->fields('dbtng_example')
  224. * ->condition('age', 18, '>')
  225. * ->execute();
  226. * @endcode
  227. *
  228. * @param array $entry
  229. * An array containing all the fields used to search the entries in the table.
  230. *
  231. * @return object
  232. * An object containing the loaded entries if found.
  233. *
  234. * @see db_select()
  235. * @see db_query()
  236. * @see http://drupal.org/node/310072
  237. * @see http://drupal.org/node/310075
  238. */
  239. function dbtng_example_entry_load($entry = array()) {
  240. // Read all fields from the dbtng_example table.
  241. $select = db_select('dbtng_example', 'example');
  242. $select->fields('example');
  243. // Add each field and value as a condition to this query.
  244. foreach ($entry as $field => $value) {
  245. $select->condition($field, $value);
  246. }
  247. // Return the result in object format.
  248. return $select->execute()->fetchAll();
  249. }
  250. /**
  251. * Render a filtered list of entries in the database.
  252. *
  253. * DBTNG also helps processing queries that return several rows, providing the
  254. * found objects in the same query execution call.
  255. *
  256. * This function queries the database using a JOIN between users table and the
  257. * example entries, to provide the username that created the entry, and creates
  258. * a table with the results, processing each row.
  259. *
  260. * SELECT
  261. * e.pid as pid, e.name as name, e.surname as surname, e.age as age
  262. * u.name as username
  263. * FROM
  264. * {dbtng_example} e
  265. * JOIN
  266. * users u ON e.uid = u.uid
  267. * WHERE
  268. * e.name = 'John' AND e.age > 18
  269. *
  270. * @see db_select()
  271. * @see http://drupal.org/node/310075
  272. */
  273. function dbtng_example_advanced_list() {
  274. $output = '';
  275. $select = db_select('dbtng_example', 'e');
  276. // Join the users table, so we can get the entry creator's username.
  277. $select->join('users', 'u', 'e.uid = u.uid');
  278. // Select these specific fields for the output.
  279. $select->addField('e', 'pid');
  280. $select->addField('u', 'name', 'username');
  281. $select->addField('e', 'name');
  282. $select->addField('e', 'surname');
  283. $select->addField('e', 'age');
  284. // Filter only persons named "John".
  285. $select->condition('e.name', 'John');
  286. // Filter only persons older than 18 years.
  287. $select->condition('e.age', 18, '>');
  288. // Make sure we only get items 0-49, for scalability reasons.
  289. $select->range(0, 50);
  290. // Now, loop all these entries and show them in a table. Note that there is no
  291. // db_fetch_* object or array function being called here. Also note that the
  292. // following line could have been written as
  293. // $entries = $select->execute()->fetchAll() which would return each selected
  294. // record as an object instead of an array.
  295. $entries = $select->execute()->fetchAll(PDO::FETCH_ASSOC);
  296. if (!empty($entries)) {
  297. $rows = array();
  298. foreach ($entries as $entry) {
  299. // Sanitize the data before handing it off to the theme layer.
  300. $rows[] = array_map('check_plain', $entry);
  301. }
  302. // Make a table for them.
  303. $header = array(t('Id'), t('Created by'), t('Name'), t('Surname'), t('Age'));
  304. $output .= theme('table', array('header' => $header, 'rows' => $rows));
  305. }
  306. else {
  307. drupal_set_message(t('No entries meet the filter criteria (Name = "John" and Age > 18).'));
  308. }
  309. return $output;
  310. }
  311. /**
  312. * Implements hook_help().
  313. *
  314. * Show some help on each form provided by this module.
  315. */
  316. function dbtng_example_help($path) {
  317. $output = '';
  318. switch ($path) {
  319. case 'examples/dbtng':
  320. $output = t('Generate a list of all entries in the database. There is no filter in the query.');
  321. break;
  322. case 'examples/dbtng/advanced':
  323. $output = t('A more complex list of entries in the database.') . ' ';
  324. $output .= t('Only the entries with name = "John" and age older than 18 years are shown, the username of the person who created the entry is also shown.');
  325. break;
  326. case 'examples/dbtng/update':
  327. $output = t('Demonstrates a database update operation.');
  328. break;
  329. case 'examples/dbtng/add':
  330. $output = t('Add an entry to the dbtng_example table.');
  331. break;
  332. }
  333. return $output;
  334. }
  335. /**
  336. * Implements hook_menu().
  337. *
  338. * Set up calls to drupal_get_form() for all our example cases.
  339. */
  340. function dbtng_example_menu() {
  341. $items = array();
  342. $items['examples/dbtng'] = array(
  343. 'title' => 'DBTNG Example',
  344. 'page callback' => 'dbtng_example_list',
  345. 'access callback' => TRUE,
  346. );
  347. $items['examples/dbtng/list'] = array(
  348. 'title' => 'List',
  349. 'type' => MENU_DEFAULT_LOCAL_TASK,
  350. 'weight' => -10,
  351. );
  352. $items['examples/dbtng/add'] = array(
  353. 'title' => 'Add entry',
  354. 'page callback' => 'drupal_get_form',
  355. 'page arguments' => array('dbtng_example_form_add'),
  356. 'access callback' => TRUE,
  357. 'type' => MENU_LOCAL_TASK,
  358. 'weight' => -9,
  359. );
  360. $items['examples/dbtng/update'] = array(
  361. 'title' => 'Update entry',
  362. 'page callback' => 'drupal_get_form',
  363. 'page arguments' => array('dbtng_example_form_update'),
  364. 'type' => MENU_LOCAL_TASK,
  365. 'access callback' => TRUE,
  366. 'weight' => -5,
  367. );
  368. $items['examples/dbtng/advanced'] = array(
  369. 'title' => 'Advanced list',
  370. 'page callback' => 'dbtng_example_advanced_list',
  371. 'access callback' => TRUE,
  372. 'type' => MENU_LOCAL_TASK,
  373. );
  374. return $items;
  375. }
  376. /**
  377. * Render a list of entries in the database.
  378. */
  379. function dbtng_example_list() {
  380. $output = '';
  381. // Get all entries in the dbtng_example table.
  382. if ($entries = dbtng_example_entry_load()) {
  383. $rows = array();
  384. foreach ($entries as $entry) {
  385. // Sanitize the data before handing it off to the theme layer.
  386. $rows[] = array_map('check_plain', (array) $entry);
  387. }
  388. // Make a table for them.
  389. $header = array(t('Id'), t('uid'), t('Name'), t('Surname'), t('Age'));
  390. $output .= theme('table', array('header' => $header, 'rows' => $rows));
  391. }
  392. else {
  393. drupal_set_message(t('No entries have been added yet.'));
  394. }
  395. return $output;
  396. }
  397. /**
  398. * Prepare a simple form to add an entry, with all the interesting fields.
  399. */
  400. function dbtng_example_form_add($form, &$form_state) {
  401. $form = array();
  402. $form['add'] = array(
  403. '#type' => 'fieldset',
  404. '#title' => t('Add a person entry'),
  405. );
  406. $form['add']['name'] = array(
  407. '#type' => 'textfield',
  408. '#title' => t('Name'),
  409. '#size' => 15,
  410. );
  411. $form['add']['surname'] = array(
  412. '#type' => 'textfield',
  413. '#title' => t('Surname'),
  414. '#size' => 15,
  415. );
  416. $form['add']['age'] = array(
  417. '#type' => 'textfield',
  418. '#title' => t('Age'),
  419. '#size' => 5,
  420. '#description' => t("Values greater than 127 will cause an exception. Try it - it's a great example why exception handling is needed with DTBNG."),
  421. );
  422. $form['add']['submit'] = array(
  423. '#type' => 'submit',
  424. '#value' => t('Add'),
  425. );
  426. return $form;
  427. }
  428. /**
  429. * Submit handler for 'add entry' form.
  430. */
  431. function dbtng_example_form_add_submit($form, &$form_state) {
  432. global $user;
  433. // Save the submitted entry.
  434. $entry = array(
  435. 'name' => $form_state['values']['name'],
  436. 'surname' => $form_state['values']['surname'],
  437. 'age' => $form_state['values']['age'],
  438. 'uid' => $user->uid,
  439. );
  440. $return = dbtng_example_entry_insert($entry);
  441. if ($return) {
  442. drupal_set_message(t("Created entry @entry", array('@entry' => print_r($entry, TRUE))));
  443. }
  444. }
  445. /**
  446. * Sample UI to update a record.
  447. */
  448. function dbtng_example_form_update($form, &$form_state) {
  449. $form = array(
  450. '#prefix' => '<div id="updateform">',
  451. '#suffix' => '</div>',
  452. );
  453. $entries = dbtng_example_entry_load();
  454. $keyed_entries = array();
  455. if (empty($entries)) {
  456. $form['no_values'] = array(
  457. '#value' => t("No entries exist in the table dbtng_example table."),
  458. );
  459. return $form;
  460. }
  461. foreach ($entries as $entry) {
  462. $options[$entry->pid] = t("@pid: @name @surname (@age)",
  463. array(
  464. '@pid' => $entry->pid,
  465. '@name' => $entry->name,
  466. '@surname' => $entry->surname,
  467. '@age' => $entry->age,
  468. )
  469. );
  470. $keyed_entries[$entry->pid] = $entry;
  471. }
  472. $default_entry = !empty($form_state['values']['pid']) ? $keyed_entries[$form_state['values']['pid']] : $entries[0];
  473. $form_state['entries'] = $keyed_entries;
  474. $form['pid'] = array(
  475. '#type' => 'select',
  476. '#options' => $options,
  477. '#title' => t('Choose entry to update'),
  478. '#default_value' => $default_entry->pid,
  479. '#ajax' => array(
  480. 'wrapper' => 'updateform',
  481. 'callback' => 'dbtng_example_form_update_callback',
  482. ),
  483. );
  484. $form['name'] = array(
  485. '#type' => 'textfield',
  486. '#title' => t('Updated first name'),
  487. '#size' => 15,
  488. '#default_value' => $default_entry->name,
  489. );
  490. $form['surname'] = array(
  491. '#type' => 'textfield',
  492. '#title' => t('Updated last name'),
  493. '#size' => 15,
  494. '#default_value' => $default_entry->surname,
  495. );
  496. $form['age'] = array(
  497. '#type' => 'textfield',
  498. '#title' => t('Updated age'),
  499. '#size' => 4,
  500. '#default_value' => $default_entry->age,
  501. '#description' => t("Values greater than 127 will cause an exception"),
  502. );
  503. $form['submit'] = array(
  504. '#type' => 'submit',
  505. '#value' => t('Update'),
  506. );
  507. return $form;
  508. }
  509. /**
  510. * AJAX callback handler for the pid select.
  511. *
  512. * When the pid changes, populates the defaults from the database in the form.
  513. */
  514. function dbtng_example_form_update_callback($form, $form_state) {
  515. $entry = $form_state['entries'][$form_state['values']['pid']];
  516. // Setting the #value of items is the only way I was able to figure out
  517. // to get replaced defaults on these items. #default_value will not do it
  518. // and shouldn't.
  519. foreach (array('name', 'surname', 'age') as $item) {
  520. $form[$item]['#value'] = $entry->$item;
  521. }
  522. return $form;
  523. }
  524. /**
  525. * Submit handler for 'update entry' form.
  526. */
  527. function dbtng_example_form_update_submit($form, &$form_state) {
  528. global $user;
  529. // Save the submitted entry.
  530. $entry = array(
  531. 'pid' => $form_state['values']['pid'],
  532. 'name' => $form_state['values']['name'],
  533. 'surname' => $form_state['values']['surname'],
  534. 'age' => $form_state['values']['age'],
  535. 'uid' => $user->uid,
  536. );
  537. $count = dbtng_example_entry_update($entry);
  538. drupal_set_message(t("Updated entry @entry (@count row updated)",
  539. array('@count' => $count, '@entry' => print_r($entry, TRUE))));
  540. }
  541. /**
  542. * @} End of "defgroup dbtng_example".
  543. */