ajax_example_autocomplete.inc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. <?php
  2. /**
  3. * @file
  4. * ajax_example_autocomplete.inc
  5. *
  6. * Demonstrates usage of the Form API's autocomplete features.
  7. *
  8. * This file provides three examples in increasing complexity:
  9. * - A simple username autocomplete (usernames are unique, so little effort is
  10. * required)
  11. * - A node title autocomplete (titles are not unique, so we have to find the
  12. * nid and stash it in the field)
  13. * - A username autocomplete that updates a node title autocomplete with a
  14. * changed #autocomplete_path so that the #autocomplete_path can have
  15. * context (the username to use in the search).
  16. */
  17. /**
  18. * A simple autocomplete form which just looks up usernames in the user table.
  19. *
  20. * @param array $form
  21. * Form API form.
  22. * @param array $form_state
  23. * Form API form.
  24. *
  25. * @return array
  26. * Form array.
  27. */
  28. function ajax_example_simple_autocomplete($form, &$form_state) {
  29. $form['info'] = array(
  30. '#markup' => '<div>' . t("This example does a simplest possible autocomplete by username. You'll need a few users on your system for it to make sense.") . '</div>',
  31. );
  32. $form['user'] = array(
  33. '#type' => 'textfield',
  34. '#title' => t('Choose a user (or a people, depending on your usage preference)'),
  35. // The autocomplete path is provided in hook_menu in ajax_example.module.
  36. '#autocomplete_path' => 'examples/ajax_example/simple_user_autocomplete_callback',
  37. );
  38. return $form;
  39. }
  40. /**
  41. * This is just a copy of user_autocomplete().
  42. *
  43. * It works simply by searching usernames (and of course in Drupal usernames
  44. * are unique, so can be used for identifying a record.)
  45. *
  46. * The returned $matches array has
  47. * * key: string which will be displayed once the autocomplete is selected
  48. * * value: the value which will is displayed in the autocomplete pulldown.
  49. *
  50. * In the simplest cases (see user_autocomplete()) these are the same, and
  51. * nothing needs to be done. However, more more complicated autocompletes
  52. * require more work. Here we demonstrate the difference by displaying the UID
  53. * along with the username in the dropdown.
  54. *
  55. * In the end, though, we'll be doing something with the value that ends up in
  56. * the textfield, so it needs to uniquely identify the record we want to access.
  57. * This is demonstrated in ajax_example_unique_autocomplete().
  58. *
  59. * @param string $string
  60. * The string that will be searched.
  61. */
  62. function ajax_example_simple_user_autocomplete_callback($string = "") {
  63. $matches = array();
  64. if ($string) {
  65. $result = db_select('users')
  66. ->fields('users', array('name', 'uid'))
  67. ->condition('name', db_like($string) . '%', 'LIKE')
  68. ->range(0, 10)
  69. ->execute();
  70. foreach ($result as $user) {
  71. // In the simplest case (see user_autocomplete), the key and the value are
  72. // the same. Here we'll display the uid along with the username in the
  73. // dropdown.
  74. $matches[$user->name] = check_plain($user->name) . " (uid=$user->uid)";
  75. }
  76. }
  77. drupal_json_output($matches);
  78. }
  79. /**
  80. * An autocomplete form to look up nodes by title.
  81. *
  82. * An autocomplete form which looks up nodes by title in the node table,
  83. * but must keep track of the nid, because titles are certainly not guaranteed
  84. * to be unique.
  85. *
  86. * @param array $form
  87. * Form API form.
  88. * @param array $form_state
  89. * Form API form state.
  90. *
  91. * * @return array
  92. * Form array.
  93. */
  94. function ajax_example_unique_autocomplete($form, &$form_state) {
  95. $form['info'] = array(
  96. '#markup' => '<div>' . t("This example does a node autocomplete by title. The difference between this and a username autocomplete is that the node title may not be unique, so we have to use the nid for uniqueness, placing it in a parseable location in the textfield.") . '</div>',
  97. );
  98. $form['node'] = array(
  99. '#type' => 'textfield',
  100. '#title' => t('Choose a node by title'),
  101. // The autocomplete path is provided in hook_menu in ajax_example.module.
  102. '#autocomplete_path' => 'examples/ajax_example/unique_node_autocomplete_callback',
  103. );
  104. $form['actions'] = array(
  105. '#type' => 'actions',
  106. );
  107. $form['actions']['submit'] = array(
  108. '#type' => 'submit',
  109. '#value' => t('Submit'),
  110. );
  111. return $form;
  112. }
  113. /**
  114. * Node title validation handler.
  115. *
  116. * Validate handler to convert our string like "Some node title [3325]" into a
  117. * nid.
  118. *
  119. * In case the user did not actually use the autocomplete or have a valid string
  120. * there, we'll try to look up a result anyway giving it our best guess.
  121. *
  122. * Since the user chose a unique node, we must now use the same one in our
  123. * submit handler, which means we need to look in the string for the nid.
  124. *
  125. * @param array $form
  126. * Form API form.
  127. * @param array $form_state
  128. * Form API form state.
  129. */
  130. function ajax_example_unique_autocomplete_validate($form, &$form_state) {
  131. $title = $form_state['values']['node'];
  132. $matches = array();
  133. // This preg_match() looks for the last pattern like [33334] and if found
  134. // extracts the numeric portion.
  135. $result = preg_match('/\[([0-9]+)\]$/', $title, $matches);
  136. if ($result > 0) {
  137. // If $result is nonzero, we found a match and can use it as the index into
  138. // $matches.
  139. $nid = $matches[$result];
  140. // Verify that it's a valid nid.
  141. $node = node_load($nid);
  142. if (empty($node)) {
  143. form_error($form['node'], t('Sorry, no node with nid %nid can be found', array('%nid' => $nid)));
  144. return;
  145. }
  146. }
  147. // BUT: Not everybody will have javascript turned on, or they might hit ESC
  148. // and not use the autocomplete values offered. In that case, we can attempt
  149. // to come up with a useful value. This is not absolutely necessary, and we
  150. // *could* just emit a form_error() as below.
  151. else {
  152. $nid = db_select('node')
  153. ->fields('node', array('nid'))
  154. ->condition('title', db_like($title) . '%', 'LIKE')
  155. ->range(0, 1)
  156. ->execute()
  157. ->fetchField();
  158. }
  159. // Now, if we somehow found a nid, assign it to the node. If we failed, emit
  160. // an error.
  161. if (!empty($nid)) {
  162. $form_state['values']['node'] = $nid;
  163. }
  164. else {
  165. form_error($form['node'], t('Sorry, no node starting with %title can be found', array('%title' => $title)));
  166. }
  167. }
  168. /**
  169. * Submit handler for node lookup unique autocomplete example.
  170. *
  171. * Here the nid has already been placed in $form_state['values']['node'] by the
  172. * validation handler.
  173. *
  174. * @param array $form
  175. * Form API form.
  176. * @param array $form_state
  177. * Form API form state.
  178. */
  179. function ajax_example_unique_autocomplete_submit($form, &$form_state) {
  180. $node = node_load($form_state['values']['node']);
  181. drupal_set_message(t('You found node %nid with title %title', array('%nid' => $node->nid, '%title' => $node->title)));
  182. }
  183. /**
  184. * Autocomplete callback for nodes by title.
  185. *
  186. * Searches for a node by title, but then identifies it by nid, so the actual
  187. * returned value can be used later by the form.
  188. *
  189. * The returned $matches array has
  190. * - key: The title, with the identifying nid in brackets, like "Some node
  191. * title [3325]"
  192. * - value: the title which will is displayed in the autocomplete pulldown.
  193. *
  194. * Note that we must use a key style that can be parsed successfully and
  195. * unambiguously. For example, if we might have node titles that could have
  196. * [3325] in them, then we'd have to use a more restrictive token.
  197. *
  198. * @param string $string
  199. * The string that will be searched.
  200. */
  201. function ajax_example_unique_node_autocomplete_callback($string = "") {
  202. $matches = array();
  203. if ($string) {
  204. $result = db_select('node')
  205. ->fields('node', array('nid', 'title'))
  206. ->condition('title', db_like($string) . '%', 'LIKE')
  207. ->range(0, 10)
  208. ->execute();
  209. foreach ($result as $node) {
  210. $matches[$node->title . " [$node->nid]"] = check_plain($node->title);
  211. }
  212. }
  213. drupal_json_output($matches);
  214. }
  215. /**
  216. * Search by title and author.
  217. *
  218. * In this example, we'll look up nodes by title, but we want only nodes that
  219. * have been authored by a particular user. That means that we'll have to make
  220. * an autocomplete function which takes a username as an argument, and use
  221. * #ajax to change the #autocomplete_path based on the selected user.
  222. *
  223. * Although the implementation of the validate handler may look complex, it's
  224. * just ambitious. The idea here is:
  225. * 1. Autcomplete to get a valid username.
  226. * 2. Use #ajax to update the node element with a #autocomplete_callback that
  227. * gives the context for the username.
  228. * 3. Do an autcomplete on the node field that is limited by the username.
  229. *
  230. * @param array $form
  231. * Form API form.
  232. * @param array $form_state
  233. * Form API form state.
  234. *
  235. * @return array
  236. * Form API array.
  237. */
  238. function ajax_example_node_by_author_autocomplete($form, &$form_state) {
  239. $form['intro'] = array(
  240. '#markup' => '<div>' . t("This example uses a user autocomplete to dynamically change a node title autocomplete using #ajax.
  241. This is a way to get past the fact that we have no other way to provide context to the autocomplete function.
  242. It won't work very well unless you have a few users who have created some content that you can search for.") . '</div>',
  243. );
  244. $form['author'] = array(
  245. '#type' => 'textfield',
  246. '#title' => t('Choose the username that authored nodes you are interested in'),
  247. // Since we just need simple user lookup, we can use the simplest function
  248. // of them all, user_autocomplete().
  249. '#autocomplete_path' => 'user/autocomplete',
  250. '#ajax' => array(
  251. 'callback' => 'ajax_example_node_by_author_ajax_callback',
  252. 'wrapper' => 'autocomplete-by-node-ajax-replace',
  253. ),
  254. );
  255. // This form element with autocomplete will be replaced by #ajax whenever the
  256. // author changes, allowing the search to be limited by user.
  257. $form['node'] = array(
  258. '#type' => 'textfield',
  259. '#title' => t('Choose a node by title'),
  260. '#prefix' => '<div id="autocomplete-by-node-ajax-replace">',
  261. '#suffix' => '</div>',
  262. '#disabled' => TRUE,
  263. );
  264. // When the author changes in the author field, we'll change the
  265. // autocomplete_path to match.
  266. if (!empty($form_state['values']['author'])) {
  267. $author = user_load_by_name($form_state['values']['author']);
  268. if (!empty($author)) {
  269. $autocomplete_path = 'examples/ajax_example/node_by_author_autocomplete/' . $author->uid;
  270. $form['node']['#autocomplete_path'] = $autocomplete_path;
  271. $form['node']['#title'] = t('Choose a node title authored by %author', array('%author' => $author->name));
  272. $form['node']['#disabled'] = FALSE;
  273. }
  274. }
  275. $form['actions'] = array(
  276. '#type' => 'actions',
  277. );
  278. $form['actions']['submit'] = array(
  279. '#type' => 'submit',
  280. '#value' => t('Submit'),
  281. );
  282. return $form;
  283. }
  284. /**
  285. * AJAX callback for author form element.
  286. *
  287. * @param array $form
  288. * Form API form.
  289. * @param array $form_state
  290. * Form API form state.
  291. *
  292. * @return array
  293. * Form API array.
  294. */
  295. function ajax_example_node_by_author_ajax_callback($form, $form_state) {
  296. return $form['node'];
  297. }
  298. /**
  299. * Validate handler to convert our title string into a nid.
  300. *
  301. * In case the user did not actually use the autocomplete or have a valid string
  302. * there, we'll try to look up a result anyway giving it our best guess.
  303. *
  304. * Since the user chose a unique node, we must now use the same one in our
  305. * submit handler, which means we need to look in the string for the nid.
  306. *
  307. * This handler looks complex because it's ambitious (and tries to punt and
  308. * find a node if they've entered a valid username and part of a title), but
  309. * you *could* just do a form_error() if nothing were found, forcing people to
  310. * use the autocomplete to look up the relevant items.
  311. *
  312. * @param array $form
  313. * Form API form.
  314. * @param array $form_state
  315. * Form API form state.
  316. *
  317. * @return array
  318. * Form API array.
  319. */
  320. function ajax_example_node_by_author_autocomplete_validate($form, &$form_state) {
  321. $title = $form_state['values']['node'];
  322. $author = $form_state['values']['author'];
  323. $matches = array();
  324. // We must have a valid user.
  325. $account = user_load_by_name($author);
  326. if (empty($account)) {
  327. form_error($form['author'], t('You must choose a valid author username'));
  328. return;
  329. }
  330. // This preg_match() looks for the last pattern like [33334] and if found
  331. // extracts the numeric portion.
  332. $result = preg_match('/\[([0-9]+)\]$/', $title, $matches);
  333. if ($result > 0) {
  334. // If $result is nonzero, we found a match and can use it as the index into
  335. // $matches.
  336. $nid = $matches[$result];
  337. // Verify that it's a valid nid.
  338. $node = node_load($nid);
  339. if (empty($node)) {
  340. form_error($form['node'], t('Sorry, no node with nid %nid can be found', array('%nid' => $nid)));
  341. return;
  342. }
  343. }
  344. // BUT: Not everybody will have javascript turned on, or they might hit ESC
  345. // and not use the autocomplete values offered. In that case, we can attempt
  346. // to come up with a useful value. This is not absolutely necessary, and we
  347. // *could* just emit a form_error() as below. Here we'll find the *first*
  348. // matching title and assume that is adequate.
  349. else {
  350. $nid = db_select('node')
  351. ->fields('node', array('nid'))
  352. ->condition('uid', $account->uid)
  353. ->condition('title', db_like($title) . '%', 'LIKE')
  354. ->range(0, 1)
  355. ->execute()
  356. ->fetchField();
  357. }
  358. // Now, if we somehow found a nid, assign it to the node. If we failed, emit
  359. // an error.
  360. if (!empty($nid)) {
  361. $form_state['values']['node'] = $nid;
  362. }
  363. else {
  364. form_error($form['node'], t('Sorry, no node starting with %title can be found', array('%title' => $title)));
  365. }
  366. }
  367. /**
  368. * Submit handler for node lookup unique autocomplete example.
  369. *
  370. * Here the nid has already been placed in $form_state['values']['node'] by the
  371. * validation handler.
  372. *
  373. * @param array $form
  374. * Form API form.
  375. * @param array $form_state
  376. * Form API form state.
  377. *
  378. * @return array
  379. * Form API array.
  380. */
  381. function ajax_example_node_by_author_autocomplete_submit($form, &$form_state) {
  382. $node = node_load($form_state['values']['node']);
  383. $account = user_load($node->uid);
  384. drupal_set_message(t('You found node %nid with title !title_link, authored by !user_link',
  385. array(
  386. '%nid' => $node->nid,
  387. '!title_link' => l($node->title, 'node/' . $node->nid),
  388. '!user_link' => theme('username', array('account' => $account)),
  389. )
  390. ));
  391. }
  392. /**
  393. * Autocomplete callback for nodes by title but limited by author.
  394. *
  395. * Searches for a node by title given the passed-in author username.
  396. *
  397. * The returned $matches array has
  398. * - key: The title, with the identifying nid in brackets, like "Some node
  399. * title [3325]"
  400. * - value: the title which will is displayed in the autocomplete pulldown.
  401. *
  402. * Note that we must use a key style that can be parsed successfully and
  403. * unambiguously. For example, if we might have node titles that could have
  404. * [3325] in them, then we'd have to use a more restrictive token.
  405. *
  406. * @param int $author_uid
  407. * The author username to limit the search.
  408. * @param string $string
  409. * The string that will be searched.
  410. */
  411. function ajax_example_node_by_author_node_autocomplete_callback($author_uid, $string = "") {
  412. $matches = array();
  413. if ($author_uid > 0 && trim($string)) {
  414. $result = db_select('node')
  415. ->fields('node', array('nid', 'title'))
  416. ->condition('uid', $author_uid)
  417. ->condition('title', db_like($string) . '%', 'LIKE')
  418. ->range(0, 10)
  419. ->execute();
  420. foreach ($result as $node) {
  421. $matches[$node->title . " [$node->nid]"] = check_plain($node->title);
  422. }
  423. }
  424. drupal_json_output($matches);
  425. }