Hero

Como identificar filas seleccionadas en un tableselect en Drupal 7

Abril 19, 2014

enzo
Drupal
Desarrollo de Modulos

En la entrada de blog Como crear un formulario múltiple dentro de una tabla en Drupal 7 vimos como usar el control de FAPI tableselect en conjunto con llamados Ajax. Hoy veremos como hacer borrado de las lineas seleccionadas.

Cuando terminamos de implementar un tableselect tendremos un resultado similar a la siguiente imagen.

tableselect con multi form 1

Como se puede apreciar el tableselect agrega un elemento checkbox a cada linea que nos permite seleccionar una linea individual, así como un elemento en el encabezado de la tabla para seleccionar todas las filas de la tabla.

Lo primero que debemos hacer es agregar un botón para procesar el formulario vía Ajax como se muestra a continuación.

$form['delete_selected'] = array
 (
   '#type' => 'button',
   '#value' => t('Delete selected'),
   '#ajax' => array(
    'callback' => 'mymodel_ajax_add_lines',
    'wrapper' => 'order_lines',
    ),
 );

Como vimos en la entrada de blog Como crear un formulario múltiple dentro de una tabla en Drupal 7 el formulario es ejecutado por completo y la función Ajax determina que secciones del formulario serán renderizadas nuevamente.

Lo que ahora debemos hacer es implementar la lógica para detectar que fue solicitado el borrado de las lineas seleccionadas para así en lugar de agregar una nueva linea, se debe borrar las lineas seleccionadas. Esto lo logramos con el siguiente código.

$deleted=0;
if(isset($form_state['triggering_element']['#value']) &&
 $form_state['triggering_element']['#value'] == 'Delete selected') {
 $lines--;
}

Con lo anterior detectamos la solicitud de borrado y evitamos que se agregue una nueva linea en blanco en nuestra tabla, así como inicializamos una variable para detectar cuantas lineas fueron borradas.

Ahora para controlar que linea fue borrada utilizaremos el siguiente código.

for ($i = 1; $i <= $lines; $i++) {
    // Remove selected lines, requiere a math due a reserve order
    // to maintain new line at the top
    if(isset($form_state['values']['order_lines']['table']) && $form_state['values']['order_lines']['table'][$form_state['storage']['lines'] - $i - 1]) {
      $deleted++;
      continue;
    }

    // OTHER CODE PROCCESS LINES
}

// Remove lines if were deleted
$form_state['storage']['lines'] = $i - $deleted;

La información sobre las lineas seleccionadas para borrar esta en la variable $form_state[‘values’][‘order_lines’][‘table’] la cual contiene un id por cada linea, pero si no esta seleccionada el valor será cero , pero si esta seleccionada será el id del fila.

En nuestro caso utilizo una formula algo complicada para calcular el indice porque la presentación del ejemplo hicimos uso de la función array_reverse para que siempre la nueva linea se coloque al inicio de la tabla, pero no es algo necesario en implementación que no hagan uso de esta técnica.

Al final vuelvo a guardar cuantas lineas quedaron luego de eliminar las lineas solicitadas.

La función completa del form quedaría como se muestra en el siguiente listado.

/*
 * Function to create form to add an order.
 */
function MIMODULO_add_order($form, &$form_state) {
 
   $form_state['storage']['lines'] = isset($form_state['storage']['lines'])? $form_state['storage']['lines']:1;
 
   $form['order_lines_wrapper'] = array(
     '#type' => 'fieldset',
     '#title' => t('Order Lines'),
     '#collapsible' => FALSE,
     '#collapsed' => FALSE,
   );
 
   $form['order_lines_wrapper']['order_lines'] = array(
     '#type' => 'container',
     '#tree' => TRUE,
     '#prefix' => '<div id="order_lines">',
     '#suffix' => '</div>',
   );
 
   $header = array (
     'product' => t('Product'),
     'price' => t('Price'),
     'quantity' => t('Quantity'),
     'subtotal' => t('Subtotal'),
   );
 
   $options = array();
 
   $lines = $form_state['storage']['lines']; 
 
   $deleted=0;
   if(isset($form_state['triggering_element']['#value']) &&
     $form_state['triggering_element']['#value'] == 'Delete selected') {
     $lines--;
   }
   
   for ($i = 1; $i <= $lines; $i++) {
    // Remove selected lines, requiere a math due a reserve order
    // to maintain new line at the top
    if(isset($form_state['values']['order_lines']['table']) && $form_state['values']['order_lines']['table'][$form_state['storage']['lines'] - $i - 1]) {
      $deleted++;
      continue;
    } 
    
    $options[$i] =array
    (
      'product' => array(
        'data' => array(
          'product_' . $i => array(
            '#type' => 'textfield',
            '#value' => isset($form_state['input']['product_' . $i])?$form_state['input']['product_' . $i]:'',
            '#name' => 'product_' . $i,
            '#size' => 40,
           )
         ),
      ),
      'price' => array(
        'data' => array(
          'price_' . $i => array(
            '#type' => 'textfield',
            '#value' => isset($form_state['input']['price_' . $i])? $form_state['input']['price_' . $i]: '',
            '#name' => 'price_' . $i,
            '#size' => 20,
           )
         ),
      ),
      'quantity' => array(
        'data' => array(
          'quantity_' . $i => array(
            '#type' => 'textfield',
            '#value' => isset($form_state['input']['quantity_' . $i])? $form_state['input']['quantity_' . $i]: '',
            '#name' => 'quantity_' . $i,
            '#size' => 10,
           )
         ),
       ),
       'subtotal' => array(
        'data' => array(
          'subtotal_' . $i => array(
            '#type' => 'textfield',
            '#value' => isset($form_state['input']['subtotal_' . $i])? $form_state['input']['subtotal_' . $i]: '',
            '#name' => 'subtotal_' . $i,
            '#size' => 20,
           )
         ),
       ),
    );
  }
 
 
  $options = array_reverse($options);
 
  $form_state['storage']['lines'] = $i;
 
  $form['order_lines_wrapper']['order_lines']['table'] = array
  (
    '#type' => 'tableselect',
    '#header' => $header,
    '#options' => $options,
    '#empty' => t('No lines found'),
    );
 
  $form['submit'] = array
    (
      '#type' => 'submit',
      '#value' => t('Save Order'),
    );
 
  $form['add_more'] = array
    (
      '#type' => 'button',
      '#value' => t('Add new line'),
      '#ajax' => array(
       'callback' => 'MIMODULO_ajax_add_lines',
       'wrapper' => 'order_lines',
       ),
    );
 
  $form['delete_selected'] = array
   (
     '#type' => 'button',
     '#value' => t('Delete selected'),
     '#ajax' => array(
       'callback' => 'mymodel_ajax_add_lines',
       'wrapper' => 'order_lines',
     ),
   ); 
  return $form;
}

Puede ver una implementación completa de este ejemplo bajando el repositorio de ejemplo https://github.com/enzolutions/mymodel, se aceptan contribuciones.

Espero que haya sido de si agrado.

Recibe consejos y oportunidades de trabajo 100% remotas y en dólares de weKnow Inc.