580 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			580 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| <?php
 | |
| /**
 | |
|  * @file
 | |
|  * This is an example outlining how a module can make use of the new DBTNG
 | |
|  * database API in Drupal 7.
 | |
|  *
 | |
|  * @todo Demonstrate transaction usage.
 | |
|  *
 | |
|  * General documentation is available at
 | |
|  * @link database Database abstraction layer documentation @endlink and
 | |
|  * at @link http://drupal.org/node/310069 @endlink.
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @defgroup dbtng_example Example: Database (DBTNG)
 | |
|  * @ingroup examples
 | |
|  * @{
 | |
|  * Database examples, including DBTNG.
 | |
|  *
 | |
|  * 'DBTNG' means 'Database: The Next Generation.' Yes, Drupallers are nerds.
 | |
|  *
 | |
|  * General documentation is available at
 | |
|  * @link database.inc database abstraction layer documentation @endlink and
 | |
|  * at @link http://drupal.org/node/310069 Database API @endlink.
 | |
|  *
 | |
|  * The several examples here demonstrate basic database usage.
 | |
|  *
 | |
|  * In Drupal 6, the recommended method to save or update an entry in the
 | |
|  * database was drupal_write_record() or db_query().
 | |
|  *
 | |
|  * In Drupal 7 and forward, the usage of db_query()
 | |
|  * for INSERT, UPDATE, or DELETE is deprecated, because it is
 | |
|  * database-dependent. Instead specific functions are provided to perform these
 | |
|  * operations: db_insert(), db_update(), and db_delete() do the job now.
 | |
|  * (Note that drupal_write_record() is also deprecated.)
 | |
|  *
 | |
|  * db_insert() example:
 | |
|  * @code
 | |
|  *   // INSERT INTO {dbtng_example} (name, surname) VALUES('John, 'Doe')
 | |
|  *   db_insert('dbtng_example')
 | |
|  *     ->fields(array('name' => 'John', 'surname' => 'Doe'))
 | |
|  *     ->execute();
 | |
|  * @endcode
 | |
|  *
 | |
|  * db_update() example:
 | |
|  * @code
 | |
|  *   // UPDATE {dbtng_example} SET name = 'Jane' WHERE name = 'John'
 | |
|  *   db_update('dbtng_example')
 | |
|  *     ->fields(array('name' => 'Jane'))
 | |
|  *     ->condition('name', 'John')
 | |
|  *     ->execute();
 | |
|  * @endcode
 | |
|  *
 | |
|  * db_delete() example:
 | |
|  * @code
 | |
|  *   // DELETE FROM {dbtng_example} WHERE name = 'Jane'
 | |
|  *   db_delete('dbtng_example')
 | |
|  *     ->condition('name', 'Jane')
 | |
|  *     ->execute();
 | |
|  * @endcode
 | |
|  *
 | |
|  * See @link database Database Abstraction Layer @endlink
 | |
|  * @see db_insert()
 | |
|  * @see db_update()
 | |
|  * @see db_delete()
 | |
|  * @see drupal_write_record()
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * Save an entry in the database.
 | |
|  *
 | |
|  * The underlying DBTNG function is db_insert().
 | |
|  *
 | |
|  * In Drupal 6, this would have been:
 | |
|  * @code
 | |
|  *   db_query(
 | |
|  *     "INSERT INTO {dbtng_example} (name, surname, age)
 | |
|  *       VALUES ('%s', '%s', '%d')",
 | |
|  *     $entry['name'],
 | |
|  *     $entry['surname'],
 | |
|  *     $entry['age']
 | |
|  *   );
 | |
|  * @endcode
 | |
|  *
 | |
|  * Exception handling is shown in this example. It could be simplified
 | |
|  * without the try/catch blocks, but since an insert will throw an exception
 | |
|  * and terminate your application if the exception is not handled, it is best
 | |
|  * to employ try/catch.
 | |
|  *
 | |
|  * @param array $entry
 | |
|  *   An array containing all the fields of the database record.
 | |
|  *
 | |
|  * @see db_insert()
 | |
|  */
 | |
| function dbtng_example_entry_insert($entry) {
 | |
|   $return_value = NULL;
 | |
|   try {
 | |
|     $return_value = db_insert('dbtng_example')
 | |
|                     ->fields($entry)
 | |
|                     ->execute();
 | |
|   }
 | |
|   catch (Exception $e) {
 | |
|     drupal_set_message(t('db_insert failed. Message = %message, query= %query',
 | |
|       array('%message' => $e->getMessage(), '%query' => $e->query_string)), 'error');
 | |
|   }
 | |
|   return $return_value;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Update an entry in the database.
 | |
|  *
 | |
|  * The former, deprecated techniques used db_query() or drupal_write_record():
 | |
|  * @code
 | |
|  *  drupal_write_record('dbtng_example', $entry, $entry['pid']);
 | |
|  * @endcode
 | |
|  *
 | |
|  * @code
 | |
|  *  db_query(
 | |
|  *    "UPDATE {dbtng_example}
 | |
|  *     SET name = '%s', surname = '%s', age = '%d'
 | |
|  *     WHERE pid = %d",
 | |
|  *     $entry['pid']
 | |
|  *  );
 | |
|  * @endcode
 | |
|  *
 | |
|  * @param array $entry
 | |
|  *   An array containing all the fields of the item to be updated.
 | |
|  *
 | |
|  * @see db_update()
 | |
|  */
 | |
| function dbtng_example_entry_update($entry) {
 | |
|   try {
 | |
|     // db_update()...->execute() returns the number of rows updated.
 | |
|     $count = db_update('dbtng_example')
 | |
|               ->fields($entry)
 | |
|               ->condition('pid', $entry['pid'])
 | |
|               ->execute();
 | |
|   }
 | |
|   catch (Exception $e) {
 | |
|     drupal_set_message(t('db_update failed. Message = %message, query= %query',
 | |
|       array('%message' => $e->getMessage(), '%query' => $e->query_string)), 'error');
 | |
|   }
 | |
|   return $count;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Delete an entry from the database.
 | |
|  *
 | |
|  * The usage of db_query is deprecated except for static queries.
 | |
|  * Formerly, a deletion might have been accomplished like this:
 | |
|  * @code
 | |
|  *  db_query("DELETE FROM {dbtng_example} WHERE pid = %d", $entry['pid]);
 | |
|  * @endcode
 | |
|  *
 | |
|  * @param array $entry
 | |
|  *   An array containing at least the person identifier 'pid' element of the
 | |
|  *   entry to delete.
 | |
|  *
 | |
|  * @see db_delete()
 | |
|  */
 | |
| function dbtng_example_entry_delete($entry) {
 | |
|   db_delete('dbtng_example')
 | |
|     ->condition('pid', $entry['pid'])
 | |
|     ->execute();
 | |
| 
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Read from the database using a filter array.
 | |
|  *
 | |
|  * In Drupal 6, the standard function to perform reads was db_query(), and
 | |
|  * for static queries, it still is.
 | |
|  *
 | |
|  * db_query() used an SQL query with placeholders and arguments as parameters.
 | |
|  *
 | |
|  * @code
 | |
|  *  // Old way
 | |
|  *  $query = "SELECT * FROM {dbtng_example} n WHERE n.uid = %d AND name = '%s'";
 | |
|  *  $result = db_query($query, $uid, $name);
 | |
|  * @endcode
 | |
|  *
 | |
|  * Drupal 7 DBTNG provides an abstracted interface that will work with a wide
 | |
|  * variety of database engines.
 | |
|  *
 | |
|  * db_query() is deprecated except when doing a static query. The following is
 | |
|  * perfectly acceptable in Drupal 7. See
 | |
|  * @link http://drupal.org/node/310072 the handbook page on static queries @endlink
 | |
|  *
 | |
|  * @code
 | |
|  *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
 | |
|  *   db_query(
 | |
|  *     "SELECT * FROM {dbtng_example} WHERE uid = :uid and name = :name",
 | |
|  *     array(':uid' => 0, ':name' => 'John')
 | |
|  *   )->execute();
 | |
|  * @endcode
 | |
|  *
 | |
|  * But for more dynamic queries, Drupal provides the db_select() API method, so
 | |
|  * there are several ways to perform the same SQL query. See the
 | |
|  * @link http://drupal.org/node/310075 handbook page on dynamic queries. @endlink
 | |
|  *
 | |
|  * @code
 | |
|  *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
 | |
|  *   db_select('dbtng_example')
 | |
|  *     ->fields('dbtng_example')
 | |
|  *     ->condition('uid', 0)
 | |
|  *     ->condition('name', 'John')
 | |
|  *     ->execute();
 | |
|  * @endcode
 | |
|  *
 | |
|  * Here is db_select with named placeholders:
 | |
|  * @code
 | |
|  *   // SELECT * FROM {dbtng_example} WHERE uid = 0 AND name = 'John'
 | |
|  *   $arguments = array(':name' => 'John', ':uid' => 0);
 | |
|  *   db_select('dbtng_example')
 | |
|  *     ->fields('dbtng_example')
 | |
|  *     ->where('uid = :uid AND name = :name', $arguments)
 | |
|  *     ->execute();
 | |
|  * @endcode
 | |
|  *
 | |
|  * Conditions are stacked and evaluated as AND and OR depending on the type of
 | |
|  * query. For more information, read the conditional queries handbook page at:
 | |
|  * http://drupal.org/node/310086
 | |
|  *
 | |
|  * The condition argument is an 'equal' evaluation by default, but this can be
 | |
|  * altered:
 | |
|  * @code
 | |
|  *   // SELECT * FROM {dbtng_example} WHERE age > 18
 | |
|  *   db_select('dbtng_example')
 | |
|  *     ->fields('dbtng_example')
 | |
|  *     ->condition('age', 18, '>')
 | |
|  *     ->execute();
 | |
|  * @endcode
 | |
|  *
 | |
|  * @param array $entry
 | |
|  *   An array containing all the fields used to search the entries in the table.
 | |
|  *
 | |
|  * @return object
 | |
|  *   An object containing the loaded entries if found.
 | |
|  *
 | |
|  * @see db_select()
 | |
|  * @see db_query()
 | |
|  * @see http://drupal.org/node/310072
 | |
|  * @see http://drupal.org/node/310075
 | |
|  */
 | |
| function dbtng_example_entry_load($entry = array()) {
 | |
|   // Read all fields from the dbtng_example table.
 | |
|   $select = db_select('dbtng_example', 'example');
 | |
|   $select->fields('example');
 | |
| 
 | |
|   // Add each field and value as a condition to this query.
 | |
|   foreach ($entry as $field => $value) {
 | |
|     $select->condition($field, $value);
 | |
|   }
 | |
|   // Return the result in object format.
 | |
|   return $select->execute()->fetchAll();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Render a filtered list of entries in the database.
 | |
|  *
 | |
|  * DBTNG also helps processing queries that return several rows, providing the
 | |
|  * found objects in the same query execution call.
 | |
|  *
 | |
|  * This function queries the database using a JOIN between users table and the
 | |
|  * example entries, to provide the username that created the entry, and creates
 | |
|  * a table with the results, processing each row.
 | |
|  *
 | |
|  * SELECT
 | |
|  *  e.pid as pid, e.name as name, e.surname as surname, e.age as age
 | |
|  *  u.name as username
 | |
|  * FROM
 | |
|  *  {dbtng_example} e
 | |
|  * JOIN
 | |
|  *  users u ON e.uid = u.uid
 | |
|  * WHERE
 | |
|  *  e.name = 'John' AND e.age > 18
 | |
|  *
 | |
|  * @see db_select()
 | |
|  * @see http://drupal.org/node/310075
 | |
|  */
 | |
| function dbtng_example_advanced_list() {
 | |
|   $output = '';
 | |
| 
 | |
|   $select = db_select('dbtng_example', 'e');
 | |
|   // Join the users table, so we can get the entry creator's username.
 | |
|   $select->join('users', 'u', 'e.uid = u.uid');
 | |
|   // Select these specific fields for the output.
 | |
|   $select->addField('e', 'pid');
 | |
|   $select->addField('u', 'name', 'username');
 | |
|   $select->addField('e', 'name');
 | |
|   $select->addField('e', 'surname');
 | |
|   $select->addField('e', 'age');
 | |
|   // Filter only persons named "John".
 | |
|   $select->condition('e.name', 'John');
 | |
|   // Filter only persons older than 18 years.
 | |
|   $select->condition('e.age', 18, '>');
 | |
|   // Make sure we only get items 0-49, for scalability reasons.
 | |
|   $select->range(0, 50);
 | |
| 
 | |
|   // Now, loop all these entries and show them in a table. Note that there is no
 | |
|   // db_fetch_* object or array function being called here. Also note that the
 | |
|   // following line could have been written as
 | |
|   // $entries = $select->execute()->fetchAll() which would return each selected
 | |
|   // record as an object instead of an array.
 | |
|   $entries = $select->execute()->fetchAll(PDO::FETCH_ASSOC);
 | |
|   if (!empty($entries)) {
 | |
|     $rows = array();
 | |
|     foreach ($entries as $entry) {
 | |
|       // Sanitize the data before handing it off to the theme layer.
 | |
|       $rows[] = array_map('check_plain', $entry);
 | |
|     }
 | |
|     // Make a table for them.
 | |
|     $header = array(t('Id'), t('Created by'), t('Name'), t('Surname'), t('Age'));
 | |
|     $output .= theme('table', array('header' => $header, 'rows' => $rows));
 | |
|   }
 | |
|   else {
 | |
|     drupal_set_message(t('No entries meet the filter criteria (Name = "John" and Age > 18).'));
 | |
|   }
 | |
|   return $output;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Implements hook_help().
 | |
|  *
 | |
|  * Show some help on each form provided by this module.
 | |
|  */
 | |
| function dbtng_example_help($path) {
 | |
|   $output = '';
 | |
|   switch ($path) {
 | |
|     case 'examples/dbtng':
 | |
|       $output = t('Generate a list of all entries in the database. There is no filter in the query.');
 | |
|       break;
 | |
| 
 | |
|     case 'examples/dbtng/advanced':
 | |
|       $output  = t('A more complex list of entries in the database.') . ' ';
 | |
|       $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.');
 | |
|       break;
 | |
| 
 | |
|     case 'examples/dbtng/update':
 | |
|       $output = t('Demonstrates a database update operation.');
 | |
|       break;
 | |
| 
 | |
|     case 'examples/dbtng/add':
 | |
|       $output = t('Add an entry to the dbtng_example table.');
 | |
|       break;
 | |
|   }
 | |
|   return $output;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Implements hook_menu().
 | |
|  *
 | |
|  * Set up calls to drupal_get_form() for all our example cases.
 | |
|  */
 | |
| function dbtng_example_menu() {
 | |
|   $items = array();
 | |
| 
 | |
|   $items['examples/dbtng'] = array(
 | |
|     'title' => 'DBTNG Example',
 | |
|     'page callback' => 'dbtng_example_list',
 | |
|     'access callback' => TRUE,
 | |
|   );
 | |
|   $items['examples/dbtng/list'] = array(
 | |
|     'title' => 'List',
 | |
|     'type' => MENU_DEFAULT_LOCAL_TASK,
 | |
|     'weight' => -10,
 | |
|   );
 | |
|   $items['examples/dbtng/add'] = array(
 | |
|     'title' => 'Add entry',
 | |
|     'page callback' => 'drupal_get_form',
 | |
|     'page arguments' => array('dbtng_example_form_add'),
 | |
|     'access callback' => TRUE,
 | |
|     'type' => MENU_LOCAL_TASK,
 | |
|     'weight' => -9,
 | |
|   );
 | |
|   $items['examples/dbtng/update'] = array(
 | |
|     'title' => 'Update entry',
 | |
|     'page callback' => 'drupal_get_form',
 | |
|     'page arguments' => array('dbtng_example_form_update'),
 | |
|     'type' => MENU_LOCAL_TASK,
 | |
|     'access callback' => TRUE,
 | |
|     'weight' => -5,
 | |
|   );
 | |
|   $items['examples/dbtng/advanced'] = array(
 | |
|     'title' => 'Advanced list',
 | |
|     'page callback' => 'dbtng_example_advanced_list',
 | |
|     'access callback' => TRUE,
 | |
|     'type' => MENU_LOCAL_TASK,
 | |
|   );
 | |
| 
 | |
|   return $items;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Render a list of entries in the database.
 | |
|  */
 | |
| function dbtng_example_list() {
 | |
|   $output = '';
 | |
| 
 | |
|   // Get all entries in the dbtng_example table.
 | |
|   if ($entries = dbtng_example_entry_load()) {
 | |
|     $rows = array();
 | |
|     foreach ($entries as $entry) {
 | |
|       // Sanitize the data before handing it off to the theme layer.
 | |
|       $rows[] = array_map('check_plain', (array) $entry);
 | |
|     }
 | |
|     // Make a table for them.
 | |
|     $header = array(t('Id'), t('uid'), t('Name'), t('Surname'), t('Age'));
 | |
|     $output .= theme('table', array('header' => $header, 'rows' => $rows));
 | |
|   }
 | |
|   else {
 | |
|     drupal_set_message(t('No entries have been added yet.'));
 | |
|   }
 | |
|   return $output;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Prepare a simple form to add an entry, with all the interesting fields.
 | |
|  */
 | |
| function dbtng_example_form_add($form, &$form_state) {
 | |
|   $form = array();
 | |
| 
 | |
|   $form['add'] = array(
 | |
|     '#type'  => 'fieldset',
 | |
|     '#title' => t('Add a person entry'),
 | |
|   );
 | |
|   $form['add']['name'] = array(
 | |
|     '#type'  => 'textfield',
 | |
|     '#title' => t('Name'),
 | |
|     '#size'  => 15,
 | |
|   );
 | |
|   $form['add']['surname'] = array(
 | |
|     '#type'  => 'textfield',
 | |
|     '#title' => t('Surname'),
 | |
|     '#size'  => 15,
 | |
|   );
 | |
|   $form['add']['age'] = array(
 | |
|     '#type'  => 'textfield',
 | |
|     '#title' => t('Age'),
 | |
|     '#size'  => 5,
 | |
|     '#description' => t("Values greater than 127 will cause an exception. Try it - it's a great example why exception handling is needed with DTBNG."),
 | |
|   );
 | |
|   $form['add']['submit'] = array(
 | |
|     '#type'  => 'submit',
 | |
|     '#value' => t('Add'),
 | |
|   );
 | |
| 
 | |
|   return $form;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Submit handler for 'add entry' form.
 | |
|  */
 | |
| function dbtng_example_form_add_submit($form, &$form_state) {
 | |
|   global $user;
 | |
| 
 | |
|   // Save the submitted entry.
 | |
|   $entry = array(
 | |
|     'name'    => $form_state['values']['name'],
 | |
|     'surname' => $form_state['values']['surname'],
 | |
|     'age'     => $form_state['values']['age'],
 | |
|     'uid'     => $user->uid,
 | |
|   );
 | |
|   $return = dbtng_example_entry_insert($entry);
 | |
|   if ($return) {
 | |
|     drupal_set_message(t("Created entry @entry", array('@entry' => print_r($entry, TRUE))));
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Sample UI to update a record.
 | |
|  */
 | |
| function dbtng_example_form_update($form, &$form_state) {
 | |
|   $form = array(
 | |
|     '#prefix' => '<div id="updateform">',
 | |
|     '#suffix' => '</div>',
 | |
|   );
 | |
| 
 | |
|   $entries = dbtng_example_entry_load();
 | |
|   $keyed_entries = array();
 | |
|   if (empty($entries)) {
 | |
|     $form['no_values'] = array(
 | |
|       '#value' => t("No entries exist in the table dbtng_example table."),
 | |
|     );
 | |
|     return $form;
 | |
|   }
 | |
| 
 | |
|   foreach ($entries as $entry) {
 | |
|     $options[$entry->pid] = t("@pid: @name @surname (@age)",
 | |
|       array(
 | |
|         '@pid' => $entry->pid,
 | |
|         '@name' => $entry->name,
 | |
|         '@surname' => $entry->surname,
 | |
|         '@age' => $entry->age,
 | |
|       )
 | |
|     );
 | |
|     $keyed_entries[$entry->pid] = $entry;
 | |
|   }
 | |
|   $default_entry = !empty($form_state['values']['pid']) ? $keyed_entries[$form_state['values']['pid']] : $entries[0];
 | |
| 
 | |
|   $form_state['entries'] = $keyed_entries;
 | |
| 
 | |
|   $form['pid'] = array(
 | |
|     '#type' => 'select',
 | |
|     '#options' => $options,
 | |
|     '#title' => t('Choose entry to update'),
 | |
|     '#default_value' => $default_entry->pid,
 | |
|     '#ajax' => array(
 | |
|       'wrapper' => 'updateform',
 | |
|       'callback' => 'dbtng_example_form_update_callback',
 | |
|     ),
 | |
|   );
 | |
| 
 | |
|   $form['name'] = array(
 | |
|     '#type' => 'textfield',
 | |
|     '#title' => t('Updated first name'),
 | |
|     '#size' => 15,
 | |
|     '#default_value' => $default_entry->name,
 | |
|   );
 | |
| 
 | |
|   $form['surname'] = array(
 | |
|     '#type' => 'textfield',
 | |
|     '#title' => t('Updated last name'),
 | |
|     '#size' => 15,
 | |
|     '#default_value' => $default_entry->surname,
 | |
|   );
 | |
|   $form['age'] = array(
 | |
|     '#type' => 'textfield',
 | |
|     '#title' => t('Updated age'),
 | |
|     '#size' => 4,
 | |
|     '#default_value' => $default_entry->age,
 | |
|     '#description' => t("Values greater than 127 will cause an exception"),
 | |
|   );
 | |
| 
 | |
|   $form['submit'] = array(
 | |
|     '#type' => 'submit',
 | |
|     '#value' => t('Update'),
 | |
|   );
 | |
|   return $form;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * AJAX callback handler for the pid select.
 | |
|  *
 | |
|  * When the pid changes, populates the defaults from the database in the form.
 | |
|  */
 | |
| function dbtng_example_form_update_callback($form, $form_state) {
 | |
|   $entry = $form_state['entries'][$form_state['values']['pid']];
 | |
|   // Setting the #value of items is the only way I was able to figure out
 | |
|   // to get replaced defaults on these items. #default_value will not do it
 | |
|   // and shouldn't.
 | |
|   foreach (array('name', 'surname', 'age') as $item) {
 | |
|     $form[$item]['#value'] = $entry->$item;
 | |
|   }
 | |
|   return $form;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Submit handler for 'update entry' form.
 | |
|  */
 | |
| function dbtng_example_form_update_submit($form, &$form_state) {
 | |
|   global $user;
 | |
| 
 | |
|   // Save the submitted entry.
 | |
|   $entry = array(
 | |
|     'pid' => $form_state['values']['pid'],
 | |
|     'name' => $form_state['values']['name'],
 | |
|     'surname' => $form_state['values']['surname'],
 | |
|     'age' => $form_state['values']['age'],
 | |
|     'uid' => $user->uid,
 | |
|   );
 | |
|   $count = dbtng_example_entry_update($entry);
 | |
|   drupal_set_message(t("Updated entry @entry (@count row updated)",
 | |
|     array('@count' => $count, '@entry' => print_r($entry, TRUE))));
 | |
| }
 | |
| /**
 | |
|  * @} End of "defgroup dbtng_example".
 | |
|  */
 |