How to make simple sortable table using sfPropel15Plugin

This article has moved to this location.


One of the features offered by sfPropel15Plugin is sortable behavior. This behavior allows a model to become an ordered list, and provides numerous methods to traverse this list in an efficient way (as said in the doc ;)). In this article, i would like to share how to implement the sortable behavior, and add the AJAX support to allow direct drag-and-drop ordering.
Sortable table

Sortable table


Schema and Fixtures

Below are schema and fixture used in this article

<?xml version="1.0" encoding="UTF-8"?>
<database name="propel" defaultIdMethod="native" noxsd="true" package="lib.model.TestSortable">
  <table name="test_item">
    <column name="id" type="INTEGER" required="true" primaryKey="true" autoincrement="true" />
    <column name="name" type="VARCHAR" size="255" required="true" />
    <behavior name="sortable" />
  </table>
</database>
TestItem:
<?php for ($i = 1; $i <= 10; $i++): ?>
  test_item_<?php echo $i ?>:
    name:      Test Item <?php echo $i."\n" ?>
<?php endfor ?>

Make sure you build your model and load the fixture once the data structure is defined by typing in a command line interface: ./symfony propel:build-all-load, then initiate the module by typing : ./symfony propel:generate-admin backend –module=test_sortable TestItem

Non-AJAX sortable table

First we will add two actions, up and down, so that the user can control the order for each item by clicking one of the two actions above.

  • Open the generator.yml file, then modify the list section, so it’ll look like below:
          list:
            display: [ name ]
            query_methods: [ orderByRank ]
            object_actions:
              _edit: ~
              _delete: ~
              up: { label: Up }
              down: {label: Down }
    
  • To enhance the interface, lets change the icon for up and down actions instead of the default one. Add the style below to the main.css:
    /*sortable*/
    #sf_admin_container ul li.sf_admin_action_up a
    {
      background: url(/images/arrow_top.gif) no-repeat 0 0;
    }
    #sf_admin_container ul li.sf_admin_action_down a
    {
      background: url(/images/arrow_down.gif) no-repeat 0 0;
    }
    
  • Initialize the cache directory by visiting the test_sortable module from your browser. Copy _list_td_actions.php file from generated cache directory into test_sortable templates module. Disable the up action for the first item and down action for the last one. Below is the final code.
    <td>
      <ul class="sf_admin_td_actions">
        <?php echo $helper->linkToEdit($TestItem, array(  'params' =>   array(  ),  'class_suffix' => 'edit',  'label' => 'Edit',)) ?>
        <?php echo $helper->linkToDelete($TestItem, array(  'params' =>   array(  ),  'confirm' => 'Are you sure?',  'class_suffix' => 'delete',  'label' => 'Delete',)) ?>
        <?php if (!$TestItem->isFirst()): ?>
        <li class="sf_admin_action_up">
          <?php echo link_to(__('Up', array(), 'messages'), 'test_sortable/ListUp?id='.$TestItem->getId(), array()) ?>
        </li>
        <?php endif; ?>
        <?php if (!$TestItem->isLast()): ?>
        <li class="sf_admin_action_down">
          <?php echo link_to(__('Down', array(), 'messages'), 'test_sortable/ListDown?id='.$TestItem->getId(), array()) ?>
        </li>
        <?php endif; ?>
      </ul>
    </td>
    
  • Next we have to define executeListUp() and executeListDown() functions in the actions.class.php
      public function executeListUp(sfWebRequest $request)
      {
        $TestItem = $this->getRoute()->getTestItem();
        $TestItem->moveUp();
        $this->redirect('@test_item');
      }
    
      public function executeListDown(sfWebRequest $request)
      {
        $TestItem = $this->getRoute()->getTestItem();
        $TestItem->moveDown();
        $this->redirect('@test_item');
      }
    

    The moveUp() and moveDown() functions are built in functions generated by sortable behavior.

The sortable table is now fully functional. Try it by moving items up and down.

Adding AJAX support

Before we begin, let us prepare the required scripts and stylesheets:

  1. jQuery TableDnD plugin (put into web/js directory).
  2. jQuery Ajax Loader plugin.

    • Download and put jquery.ajaxLoader.js file into web/js directory
    • Download and put jquery.ajaxLoader.css file into web/css directory, and change its image url by /images/jquery.ajaxLoader.gif
      div.jquery-ajax-loader {
        background: #333 url(/images/jquery.ajaxLoader.gif) no-repeat 50% 50%;
        opacity: .6;
      }
      
    • Download and put jquery.ajaxLoader.gif into web/images directory
  3. css style for confirmation message (taken from here). You can place this css style in the main.css file.

    #confirmationMessage {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 70px;
      font: normal 1.4em arial;
      line-height: 70px;
      text-align: center;
      color: #000;
      background: #fff;
      border-bottom: 1px solid #ddd;
      filter:alpha(opacity=92);
      -moz-opacity: 0.92;
      opacity: 0.92;
      display: none;
    }
    

Now that everything is ready, let’s get started.

  • Copy _list.php file from generated cache directory into test_sortable templates module, add the code below. If you’ve already loaded jQuery library, remove the first line.
    <?php use_javascript('/js/jquery-1.4.4.min.js', 'last') ?>
    <?php use_javascript('/js/jquery.ajaxLoader.js', 'last') ?>
    <?php use_javascript('/js/jquery.tablednd.js', 'last') ?>
    <?php use_stylesheet('/css/jquery.ajaxLoader.css', 'last') ?>
    <div id="confirmationMessage"></div>
    

    Define an id for tbody and tr tags, which will be used as parameters for the AJAX request.

         <tbody id="table_sortable">
            <?php foreach ($pager->getResults() as $i => $TestItem): $odd = fmod(++$i, 2) ? 'odd' : 'even' ?>
              <tr class="sf_admin_row <?php echo $odd ?>" id="<?php echo $TestItem->getId() ?>">
                <?php include_partial('test_sortable/list_td_batch_actions', array('TestItem' => $TestItem, 'helper' => $helper)) ?>
                <?php include_partial('test_sortable/list_td_tabular', array('TestItem' => $TestItem)) ?>
                <?php include_partial('test_sortable/list_td_actions', array('TestItem' => $TestItem, 'helper' => $helper)) ?>
              </tr>
            <?php endforeach; ?>
          </tbody>
    

    Add the script to handle the user drag and drop

    <script type="text/javascript">
    jQuery(document).ready(function() {
      jQuery('#table_sortable').tableDnD({
        onDrop: function (table, row) {
          jQuery.ajax({
            type:'POST',
            dataType:'json',
            data:jQuery.tableDnD.serialize(),
            success:function(response){
              jQuery('#table_sortable').ajaxLoaderRemove();
              jQuery('#confirmationMessage').html(response.message).delay(400)
                                            .slideDown(400).delay(3000).slideUp(400);
            },
            beforeSend:function(){
              jQuery('#table_sortable').ajaxLoader();
            },
            error : function(request, textStatus, errorThrown) {
              jQuery('#table_sortable').ajaxLoaderRemove();
              jQuery('#confirmationMessage').html('Oooops!!!.. A problem occurs when ordering items.').delay(400).slideDown(400).delay(3000).slideUp(400);
            },
            url:"<?php echo url_for('test_sortable/ajaxSortable') ?>"});
            return false;
        }
      });
    });
    </script>
    
  • Once the user drags an item and releases it to reorder the table, an AJAX request is made with the following parameters: tableId[]=rowId1&tableId[]=rowId2&tableId[]=rowId3…. Let us add executeAjaxSortable() function to our actions.class.php to serve the AJAX request.

      public function executeAjaxSortable(sfWebRequest $request)
      {
        $this->forward404Unless($request->isXmlHttpRequest());
        $items = $request->getParameter('table_sortable');
    
        $data = array();
        $data['error'] = false;
        $data['message'] = 'The items was ordered successfully.';
    
        //start: create array id ==> order
        $newOrders = array();
        $i = 1;
        foreach ($items as $item)  {  $newOrders[$item] = $i++; }
        //end: create array id ==> order
    
        try
        {
          TestItemQuery::create()->reorder($newOrders);
        }
        catch (Exception $e)
        {
          $data['error'] = true;
          $data['message'] = 'A problem occurs when ordering items';
        }
    
        sfConfig::set('sf_web_debug', false);
        $this->getResponse()->setContentType('application/json');
        return $this->renderText(json_encode($data));	
      }
    

    The reorder function expect the parameter in the format id/position

That’s it!. Refresh your browser, and try to drag and drop a row..

Thanks you

Advertisements

One response to “How to make simple sortable table using sfPropel15Plugin

  1. Pingback: How to make simple sortable table using sfPropel15Plugin « Creation site internet Lyon et Paris

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: