fields('p', array('nid', 'booking_firstname', 'booking_lastname')); $db_or = db_or() ->condition('p.booking_lastname', '%' . db_like($string) . '%', 'LIKE') ->condition('p.booking_firstname', '%' . db_like($string) . '%', 'LIKE') ->condition('p.nid', $string, "="); $db_and = db_and()->condition($db_or)->condition('p.booking_eventid', $event->eid, '='); $result = $query->condition($db_and) ->execute(); // save the query to matches foreach ($result as $row) { $name = $row->booking_lastname . ', ' . $row->booking_firstname . ' [' . $row->nid . ']'; $matches[$name] = $name; } // Return the result to the form in json drupal_json_output($matches); } /** * Test function for allocating people to rooms with an autocomplete field * Using a regular table of elements as per https://passingcuriosity.com/2011/drupal-7-forms-tables/ */ function booking_rooms_allocate_form($node, &$form_state, $location_id) { global $event; $form = array(); $attendee_select = array(); $options = array(); $counter = 0; $prefix = "

Enter part of a person's name in the text field and wait for the blue spinning circle to autocomplete with the person's details. Make sure you click on the person from the dropdown list that appears.

"; $form['first_para'] = array ( '#type' => 'markup', '#markup' => $prefix, ); //verify that $location_id is a number if (! preg_match('/^[0-9]+$/', $location_id)) { drupal_set_message("Error: Invalid room location ID '" . $location_id . "' supplied. Unable to allocate rooms.", 'error', FALSE); drupal_goto('admin/booking/rooms'); return ""; } //query for existing room allocations $query = db_select('booking_person', 'p'); $query->leftJoin('booking_room_mapping', 'm', 'm.booking_nodeid = p.nid'); $query->condition('p.booking_eventid', $event->eid, '='); $query->fields('p', array('booking_lastname', 'booking_firstname'))->fields('m'); $room_mapping = $query->execute()->fetchAllAssoc('booking_nodeid'); //query for room definitions $room_query = db_query("SELECT r.*, l.* FROM {booking_room_definition} r " . "INNER JOIN {booking_room_locations} l on r.booking_room_location_id = l.lid " . "WHERE booking_room_location_id = :lid " . "ORDER BY CAST(booking_room_number as SIGNED INTEGER) ASC", array(':lid' => $location_id)); //define the table header $header = array ( 'booking_room_location' => array('data' => t('Room Location'), 'field' => 'booking_room_location_id'), 'booking_room_number' => array('data' => t('Room Number')), 'booking_room_singlebed' => array('data' => t('Single Bed')), 'booking_room_doublebed_p1' => array('data' => t('Double Bed Person 1')), 'booking_room_doublebed_p2' => array('data' => t('Double Bed Person 2')), 'booking_room_queenbed_p1' => array('data' => t('Queen Bed Person 1')), 'booking_room_queenbed_p2' => array('data' => t('Queen Bed Person 2')), ); //attach the custom css $form['#attached']['css'] = array( drupal_get_path('module', 'booking') . '/booking.css', ); //create the container element for the whole table $form['rooms'] = array( '#prefix' => '
', '#suffix' => '
', '#tree' => TRUE, '#theme' => 'table', '#header' => $header, '#rows' => array(), ); //define the default fields in a table row $default_row = array(); $default_row['booking_room_location'] = ""; $default_row['booking_room_number'] = ""; $default_row['booking_room_singlebed'] = ""; $default_row['booking_room_doublebed_p1'] = ""; $default_row['booking_room_doublebed_p2'] = ""; $default_row['booking_room_queenbed_p1'] = ""; $default_row['booking_room_queenbed_p2'] = ""; foreach ($room_query as $data) { //create an array representing the existing bed mappings for this room $existing_beds = array(); for ($i = 1; $i <= 3; $i++) { foreach ($room_mapping as $mapping) { //check that the room id in the mapping table matches the room that we're currently adding to the table //and also the bed type matches the first dimension in the array if ($mapping->booking_roomid == $data->rid && $mapping->booking_room_bedtype == $i) { $existing_beds[$i][] = $mapping->booking_lastname . ', ' . $mapping->booking_firstname . ' [' . $mapping->booking_nodeid . ']'; } } } //create a row that contains just the room location and number and the custom css id for a separating line between the rooms $new_row = _booking_clone_array($default_row); //$new_row['booking_room_location'] = _booking_room_location_lookup($data->booking_room_location_id); $new_row['booking_room_location'] = $data->booking_roomlocation_descrip; $new_row['booking_room_number'] = $data->booking_room_number; $form['rooms']['#rows'][$counter++] = array( 'data' => $new_row, 'id' => array("new-group-row"), ); //create an additional row for each single bed //remove deprecated pass-by reference as per http://stackoverflow.com/questions/8971261/php-5-4-call-time-pass-by-reference-easy-fix-available _booking_rooms_allocate_generate_singlebeds($data, $existing_beds, $default_row, $counter, $form); //create an additional row for each double bed _booking_rooms_allocate_generate_doublebeds($data, $existing_beds, $default_row, $counter, $form); //create an additional row for each queen bed _booking_rooms_allocate_generate_queenbeds($data, $existing_beds, $default_row, $counter, $form); } //end foreach $room_query $form['submit'] = array ( '#type' => 'submit', '#value' => t('Submit'), ); return array ( 'form' => $form, ); } //end booking_rooms_allocate_test_form /** * Process the submission for room assignment */ function booking_rooms_allocate_form_submit($form, &$form_state) { global $event; $values = $form_state['input']; //watchdog('booking_debug', "
Room assignment test submission form :\n@info
", array('@info' => print_r( $form, true))); //query for existing room allocations $query = db_select('booking_person', 'p'); $query->leftJoin('booking_room_mapping', 'm', 'm.booking_nodeid = p.nid'); $query->condition('p.booking_eventid', $event->eid, '='); $query->fields('p', array('booking_lastname', 'booking_firstname'))->fields('m'); $room_mapping = $query->execute()->fetchAllAssoc('booking_nodeid'); $bed_inputs = array( 'booking_room_singlebed' => 1, 'booking_room_doublebed_p1' => 2, 'booking_room_doublebed_p2' => 2, 'booking_room_queenbed_p1' => 3, 'booking_room_queenbed_p2' => 3, ); //go through the different bed types foreach ($bed_inputs as $type => $type_id) { //if this bed type wasn't defined in the form, skip it if (empty($values[$type])) continue; //watchdog('booking_debug', "
Room assignment submission for !type:\n@info
", array('!type' => $type, '@info' => print_r( $values[$type], true))); //go through each room foreach($values[$type] as $room => $data) { //go through each bed foreach ($data as $bed_index => $person) { //check for previous value $previous_nid = 0; $previous_nid = _booking_rooms_allocate_get_previous_value($form, $type, $room, $type . '[' . $room . '][' . $bed_index . ']'); if (! empty($person)) { //extract nid from $person using regex if (preg_match('/[\s\w,]+\s\[(\d+)\]/i', $person, $matches)) { //watchdog('booking_debug', "Processing room assignment for ID !id belonging to person !person ", array('!id' => $matches[1], '!person' => $person)); $nid = $matches[1]; //handle the insert/update/deletes required _booking_rooms_allocate_form_submit_helper($room_mapping, $room, $type_id, $bed_index, $nid, $previous_nid); } //parsed node id successfully } //empty bed check elseif (empty($person) && $previous_nid > 0) { $message = t('Removing person !person previously in room id !room with bed index !index and bed type !type.', array('!room' => $room, '!index' => $bed_index, '!person' => $previous_nid, '!type' => $type_id)); watchdog('booking', $message); drupal_set_message($message); db_delete('booking_room_mapping') ->condition('booking_eventid', $event->eid) ->condition('booking_nodeid', $previous_nid) ->condition('booking_room_bedtype', $type_id) ->condition('booking_roomid', $room) ->execute(); } else { //TODO: Check if this bed used to have someone allocated //and if so, remove their mapping } } //next bed } //next room } //next bed type } //end function /** * look through the previous form data and return the matching element */ function _booking_rooms_allocate_get_previous_value(&$form, $type, $room, $name) { foreach($form['form']['rooms']['#rows'] as $key => $value) { //watchdog('booking_debug', "
Room assignment checker for type !type in room !room:\n@info
", array('!room' => $room, '!type' => $type, '@info' => print_r( $value, true))); //return; if ((!empty($value[$type]['data']['#value'])) && ($value[$type]['data']['#name'] == $name)) { //watchdog('booking_debug', "Found correct room with room number !num and type !type", array('!num' => $room, '!type' => $type)); //found the correct element, extract the node id $person = $value[$type]['data']['#value']; if (preg_match('/[\s\w,]+\s\[(\d+)\]/i', $person, $matches)) { return $matches[1]; } } } //in case there was no matching value, return an empty string return 0; } /** * function to update person with correct bed allocation and remove any previous allocation if necessary */ function _booking_rooms_allocate_form_submit_helper(&$room_mapping, $room, $type_id, $bed_index, $nid, $previous_nid) { global $event; $message = ""; //remove any person previously defined for this bed that doesn't match what is now defined if ($previous_nid > 0 && $nid != $previous_nid) { $message = t('Bed allocation for room !room and bed index !index has changed. Removing previous !person from this location.', array('!room' => $room, '!index' => $bed_index, '!person' => $previous_nid)); watchdog('booking', $message); drupal_set_message($message); //look for an exact match, //in case this person has moved to a different bed type during this form submission db_delete('booking_room_mapping') ->condition('booking_eventid', $event->eid) ->condition('booking_nodeid', $previous_nid) ->condition('booking_room_bedtype', $type_id) ->condition('booking_roomid', $room) ->execute(); } //if this person didn't previously have a room/bed mapping if (empty($room_mapping[$nid])) { //Validate that there is capacity for the person to be allocated to this room if (_booking_room_capacity_check($room, $type_id)) { $message = t('Assigning person id !id to a type !type bed in room id !room.', array('!id' => $nid, '!room' => $room, '!type' => $type_id)); //double check we haven't already allocated a bed during this submission $check = db_query("SELECT * FROM {booking_room_mapping} " . " WHERE booking_eventid = :eid AND booking_roomid = :rid " . " AND booking_room_bedtype = :type AND booking_nodeid = :nid", array(':eid' => $event->eid, ':rid' => $room, ':type' => $type_id, ':nid' => $nid, ))->fetchObject(); if (! $check) { $result = db_insert('booking_room_mapping') ->fields(array( 'booking_roomid' => $room, 'booking_eventid' => $event->eid, 'booking_nodeid' => $nid, 'booking_room_bedtype' => $type_id, )) ->execute(); } //this person has already been inserted during this form submission //so don't add them in twice else { $message .= t(' Except this person already exists.'); } } //no capacity available in this room else { $message = t('No capacity to assign person id !id to a type !type bed in room id !room.', array('!id' => $nid, '!room' => $room, '!type' => $type_id) ); } } //this person previously had a room mapping but to a different room elseif ((!empty($room_mapping[$nid])) && $room_mapping[$nid]->booking_roomid != $room) { $message = t('Changing person id !id from old room !oldroom to new room !room with type !type bed.', array('!id' => $nid, '!room' => $room, '!type' => $type_id, '!oldroom' => $room_mapping[$nid]->booking_roomid)); db_update('booking_room_mapping') ->fields(array( 'booking_roomid' => $room, 'booking_room_bedtype' => $type_id, )) ->condition('booking_eventid', $event->eid) ->condition('booking_nodeid', $nid) ->execute(); } //this person previously had a room mapping but to a different bed type in the same room elseif ((!empty($room_mapping[$nid])) && $room_mapping[$nid]->booking_room_bedtype != $type_id) { $message = t('Changing person id !id in room !room to new bed type type !type .', array('!id' => $nid, '!room' => $room, '!type' => $type_id, '!oldroom' => $room_mapping[$nid]->booking_roomid)); db_update('booking_room_mapping') ->fields(array( 'booking_roomid' => $room, 'booking_room_bedtype' => $type_id, )) ->condition('booking_eventid', $event->eid) ->condition('booking_nodeid', $nid) ->execute(); } //log the result if there was one if ($message !== "") { watchdog('booking', $message); drupal_set_message($message); } } /** * function to generate table rows for each single bed defined in this room */ function _booking_rooms_allocate_generate_singlebeds($data, $existing_beds, $default_row, &$counter, &$form) { //create an additional row for each single bed for ($i = 0; $i < $data->booking_room_singlebeds; $i++) { $single_bed = array ( '#id' => 'booking-room-singlebed-' . $data->rid . '-' . $i, '#type' => 'textfield', '#title' => 'Name', '#title_display' => 'invisible', '#name' => 'booking_room_singlebed[' . $data->rid . '][' . $i . ']', '#size' => 100, '#autocomplete_path' => 'booking/rooms/autocomplete', '#value' => (!empty($existing_beds[1][$i])) ? $existing_beds[1][$i] : '', '#attributes' => array('style' => array('width:200px')), ); $form['rooms'][$data->rid][$counter] = array( 'booking-room-singlebed' => &$single_bed, ); $new_row = _booking_clone_array($default_row); $new_row['booking_room_singlebed'] = array('data' => &$single_bed); $form['rooms']['#rows'][$counter] = $new_row; unset($single_bed); $counter++; } } /** * function to generate table rows for each double bed defined in this room */ function _booking_rooms_allocate_generate_doublebeds($data, $existing_beds, $default_row, &$counter, &$form) { //create an additional row for each double bed //$j is our counter that increments twice as fast as $i to cater for both beds $j = 0; for ($i = 0; $i < $data->booking_room_doublebeds; $i++) { $double_bed_p1 = array ( '#id' => 'booking-room-doublebed-p1-' . $data->rid . '-' . $i, '#type' => 'textfield', '#title' => 'Name', '#title_display' => 'invisible', '#name' => 'booking_room_doublebed_p1[' . $data->rid . '][' . $i . ']', '#size' => 100, '#autocomplete_path' => 'booking/rooms/autocomplete', '#value' => (!empty($existing_beds[2][$j])) ? $existing_beds[2][$j++] : '', '#attributes' => array('style' => array('width:200px')), ); $double_bed_p2 = array ( '#id' => 'booking-room-doublebed-p2-' . $data->rid . '-' . $i, '#type' => 'textfield', '#title' => 'Name', '#title_display' => 'invisible', '#name' => 'booking_room_doublebed_p2[' . $data->rid . '][' . $i . ']', '#size' => 100, '#autocomplete_path' => 'booking/rooms/autocomplete', '#value' => (!empty($existing_beds[2][$j])) ? $existing_beds[2][$j++] : '', '#attributes' => array('style' => array('width:200px')), ); //include fields for rendering $form['rooms'][$data->rid][$counter] = array( 'booking-room-doublebed-p1' => &$double_bed_p1, 'booking-room-doublebed-p2' => &$double_bed_p2, ); //add references to the fields for the row so that theme_table can theme appropriately $new_row = _booking_clone_array($default_row); $new_row['booking_room_doublebed_p1'] = array('data' => &$double_bed_p1); $new_row['booking_room_doublebed_p2'] = array('data' => &$double_bed_p2); $form['rooms']['#rows'][$counter] = $new_row; unset($double_bed_p1); unset($double_bed_p2); $counter++; } } /** * function to generate table rows for each queen bed defined in this room */ function _booking_rooms_allocate_generate_queenbeds($data, $existing_beds, $default_row, &$counter, &$form) { //create an additional row for each double bed //$j is our counter that increments twice as fast as $i to cater for both beds $j = 0; for ($i = 0; $i < $data->booking_room_queenbeds; $i++) { //calculate the two form elements for queen beds $queen_bed_p1 = array ( '#id' => 'booking-room-queenbed-p1-' . $data->rid . '-' . $i, '#type' => 'textfield', '#title' => 'Name', '#title_display' => 'invisible', '#name' => 'booking_room_queenbed_p1[' . $data->rid . '][' . $i . ']', '#size' => 100, '#autocomplete_path' => 'booking/rooms/autocomplete', '#value' => (!empty($existing_beds[3][$j])) ? $existing_beds[3][$j++] : '', '#attributes' => array('style' => array('width:200px')), ); $queen_bed_p2 = array ( '#id' => 'booking-room-queenbed-p2-' . $data->rid . '-' . $i, '#type' => 'textfield', '#title' => 'Name', '#title_display' => 'invisible', '#name' => 'booking_room_queenbed_p2[' . $data->rid . '][' . $i . ']', '#size' => 100, '#autocomplete_path' => 'booking/rooms/autocomplete', '#value' => (!empty($existing_beds[3][$j])) ? $existing_beds[3][$j++] : '', '#attributes' => array('style' => array('width:200px')), ); // Include the fields so they'll be rendered and named // correctly, but they'll be ignored here when rendering as // we're using #theme => table. $form['rooms'][$data->rid][$counter] = array( 'booking-room-queenbed-p1' => &$queen_bed_p1, 'booking-room-queenbed-p2' => &$queen_bed_p2, ); // Now add references to the fields to the rows that // `theme_table()` will use. $new_row = _booking_clone_array($default_row); $new_row['booking_room_queenbed_p1'] = array('data' => &$queen_bed_p1); $new_row['booking_room_queenbed_p2'] = array('data' => &$queen_bed_p2); $form['rooms']['#rows'][$counter] = $new_row; unset($queen_bed_p1); unset($queen_bed_p2); $counter++; } }