Subscriptions Batch Table Implementation

Overview

The subscriptions batch table is implemented using the DataTables library to provide a dynamic, server-side processed table for managing shipping batches. This implementation includes features like inline editing, reshipping capabilities, and real-time data updates.

Table Structure

Frontend Template

The table is rendered using a PHP template that defines the basic structure:

<table id='table-batch' class='display' data-batch-id='<?php echo $batch_id?>'>
  <thead>
    <tr>
      <?php foreach ($columns as $key => $column) {
        printf('<th>%1$s</th>', $column);
      } ?>
    </tr>
  </thead>
  <tfoot>
    <tr>
      <?php foreach ($columns as $key => $column) {
        printf('<th>%1$s</th>', $column);
      } ?>
    </tr>
  </tfoot>
</table>

Column Definitions

The table includes the following columns:

$columns = array(
    '_actions' => __('Actions', 'tsj-subscriptions'),
    '_subscription_id' => __('Sub #', 'tsj-subscriptions'),
    '_quantity' => __('Quantity', 'tsj-subscriptions'),
    '_first_name' => __('First Name', 'tsj-subscriptions'),
    '_last_name' => __('Last Name', 'tsj-subscriptions'),
    '_company' => __('Company', 'tsj-subscriptions'),
    '_address_1' => __('Address 1', 'tsj-subscriptions'),
    '_address_2' => __('Address 2', 'tsj-subscriptions'),
    '_city' => __('City', 'tsj-subscriptions'),
    '_state' => __('State', 'tsj-subscriptions'),
    '_postcode' => __('Postcode', 'tsj-subscriptions'),
    '_country' => __('Country', 'tsj-subscriptions'),
    '_country_name' => __('Country Name', 'tsj-subscriptions'),
    '_reship' => __('Reship', 'tsj-subscriptions'),
    '_reship_reason' => __('Reship Reason', 'tsj-subscriptions'),
    '_sku' => __('SKU', 'tsj-subscriptions'),
    '_first_issue' => __('First Issue', 'tsj_subscriptions'),
    '_last_issue' => __('Last Issue', 'tsj-subscriptions'),
    '_gifted' => __('Gifted', 'tsj-subscriptions'),
    '_comp' => __('Comp', 'tsj-subscriptions'),
    '_special' => __('Special', 'tsj-subscriptions'),
    '_premium' => __('Premium', 'tsj-subscriptions'),
    '_concierge' => __('Concierge', 'tsj-subscriptions'),
    '_concierge_notes' => __('Concierge Notes', 'tsj-subscriptions'),
    '_customer_note' => __('Customer Note', 'tsj-subscriptions'),
);

JavaScript Implementation

The table is initialized with the following configuration:

var table = $('#table-batch').DataTable({
    columns: [
        {data: '_actions'},
        {data: '_subscription_id'},
        {data: '_quantity'},
        // ... other columns
    ],
    serverSide: true,
    scrollX: true,
    searchDelay: 1000,
    ordering: false,
    ajax: {
        url: tsj_subscriptions_public.ajaxurl,
        data: function(d) {
            d.action = 'tsj_subscriptions_batch_get_data';
            d.shipping_batch_id = $('#table-batch').data('batch-id');
        }
    }
});

Inline Editing

The table supports inline editing using DataTables Editor:

var editor = new $.fn.dataTable.Editor({
    table: '#table-batch',
    idSrc: "id",
    ajax: {
        url: tsj_subscriptions_public.ajaxurl,
        data: function(d) {
            d.action = 'tsj_subscriptions_batch_get_data';
            d.shipping_batch_id = $('#table-batch').data('batch-id');
        }
    }
});

$('#table-batch').on('click', 'tbody td:not(:first-child)', function(e) {
    editor.inline(this);
});

Reshipping Functionality

The table includes a reshipping feature:

$.fn.dataTable.ext.buttons.reship = {
    text: 'Reship',
    extend: 'selectedSingle',
    action: function(e, dt, node, config) {
        var data = table.row('.selected').data();
        reshipDialog.foundation('open');
    }
};

Server-Side Processing

Data Retrieval

The server-side processing is handled by the TSJ_Subscriptions_Batch_Table class:

public function get_data() {
    $draw = intval($_GET['draw']);
    $per_page = intval($_GET['length']);
    $offset = intval($_GET['start']);
    $this->batch_id = intval($_GET['shipping_batch_id']);
    
    $shipping_batch = new TSJ_Shipping_Batch($this->batch_id);
    $this->issue_id = $shipping_batch->get_issue_id();
    $this->editable = $shipping_batch->get_batch_status() != 'shipped';
    $this->reshippable = (($shipping_batch->get_batch_status() == 'shipped'));
    
    $data = array();
    $search_term = isset($_GET['search']['value']) ? $_GET['search']['value'] : '';
    
    $batch_subscriptions = tsj_get_shipping_list(
        $this->batch_id, 
        $offset, 
        $per_page, 
        'all', 
        $search_term
    );
    
    // Process and format data
    foreach ($batch_subscriptions as $batch_subscription) {
        $item = array();
        foreach ($this->columns as $key => $column) {
            if ($key == '_actions') continue;
            $func = 'get' . $key;
            $item[$key] = $batch_subscription->{$func}();
        }
        
        $actions = $this->get_actions(
            $batch_subscription->get_id(), 
            $batch_subscription->get_subscription_id()
        );
        $item['_actions'] = $actions;
        $item['_country_name'] = $batch_subscription->get_country_name();
        $item['id'] = $batch_subscription->get_id();
        $data[] = $item;
    }
    
    $total = tsj_get_shipping_list_count($this->batch_id);
    
    return array(
        'draw' => $draw,
        'recordsTotal' => $total,
        'recordsFiltered' => $total,
        'data' => $data
    );
}

Action Handling

The table supports various actions through the get_actions method:

protected function get_actions($batch_subscription_id, $subscription_id) {
    $actions = '';
    
    if ($this->editable) {
        $remove_link = add_query_arg(array(
            'batch_subscription_action' => 'remove',
            'batch_subscription_id' => $batch_subscription_id,
        ), $this->batch_subscription_url);
        
        $edit_link = add_query_arg(array(
            'batch_subscription_action' => 'edit',
            'batch_subscription_id' => $batch_subscription_id,
        ), $this->batch_subscription_url);
        
        $actions .= sprintf('<a href="%s">Edit</a>', $edit_link);
        $actions .= sprintf('<a href="%s">Remove</a>', $remove_link);
    }
    
    if ($this->reshippable) {
        $reship_link = add_query_arg(array(
            'reship_action' => 'create',
            'issue_id' => $this->issue_id,
            'subscription_id' => $subscription_id,
            'batch_id' => $this->batch_id
        ), $this->reship_url);
        
        $actions .= sprintf('<a href="%s">Reship</a>', $reship_link);
        $actions = sprintf('<span class="actions">%s</span>', $actions);
    }
    
    return $actions;
}

Best Practices

Performance Optimization

  • Server-side processing for large datasets
  • Search delay implementation to reduce server load
  • Proper indexing on database tables

User Experience

  • Inline editing for quick updates
  • Clear action buttons with appropriate permissions
  • Responsive design with horizontal scrolling

Data Integrity

  • Validation of input data
  • Proper error handling
  • Status-based action availability

Security

  • Nonce verification for actions
  • Permission checks for editing
  • Input sanitization

Usage Example

To implement a similar table:

  1. Include required scripts and styles:
wp_enqueue_script('datatables');
wp_enqueue_style('datatables');
wp_enqueue_script('journal-batch-table');
  1. Initialize the table class:
$table = TSJ_Subscriptions_Batch_Table::get_instance();
  1. Create the table HTML structure:
<table id='table-batch' class='display' data-batch-id='<?php echo $batch_id?>'>
    <!-- Table structure -->
</table>
  1. The JavaScript will automatically initialize the DataTable with the configured options.