Code snippets for symfony 1.x

Navigation

Multi sort in admin generator

Here we go !

The sort in admin generator is for a single field only, but in some complex list, it can be usefull to sort by multiple criterias. This is the main goal of this snippet. Those functions therefore override the ones of your auto-generated class. (1) And to display what are the ongoing sort criterias, you have to modify your '_list_th_tabular.php' file.

1 - In your 'action.class.php'

//
    /**
     * Add a sort criteria
     */
    protected function processSort ()
    {
        $sort = $this->getRequestParameter('sort');
        $type = $this->getRequestParameter('type');            
 
        // Register sort                 
        if ($sort) {
            $this->getUser()->setAttribute($sort, $type, 'sf_admin/produits/sort');
        }
    }    
 
    /**
     * Add the sort criterias to the query
     */
    protected function addSortCriteria(&$c)
    {
        $multisort = $this->getUser()->getAttributeHolder()->getAll('sf_admin/produits/sort');
 
        if ($multisort) {
            foreach($multisort as $sort_column => $sort_type) {
                $sort_column = Propel::getDB($c->getDbName())->quoteIdentifier($sort_column);
                if ($sort_type == 'asc') {
                    $c->addAscendingOrderByColumn($sort_column);
                }
                elseif ($sort_type == 'desc') {
                    $c->addDescendingOrderByColumn($sort_column);
                }
            }
        } else {
            // Default sort 
            $sort_column = Propel::getDB($c->getDbName())->quoteIdentifier('libelle');
            $c->addAscendingOrderByColumn($sort_column);            
        }
    }
 
    /**
     * Specific function for multi-sort
     */
    protected function processFilters ()
    {
        if ($this->getRequest()->hasParameter('filter'))
        {
            $filters = $this->getRequestParameter('filters');            
 
            // Multi-sort initialisation
            if (!is_array($filters)) {
                $this->getUser()->getAttributeHolder()->removeNamespace('sf_admin/produits/sort');
            }
 
            $this->getUser()->getAttributeHolder()->removeNamespace('sf_admin/produits/filters');
            $this->getUser()->getAttributeHolder()->add($filters, 'sf_admin/produits/filters');
        }
    }

2 - In '_list_th_tabular.php'

(for each sortable field)

<?php 
 
$multisort = $sf_user->getAttributeHolder()->getAll('sf_admin/produits/sort');
 
?>
 
    <th id="sf_admin_list_th_libelle">
        <?php 
        if (isset($multisort['libelle'])) {
            echo link_to('Libellé', 'Produits/list?sort=libelle&type='. ($multisort['libelle'] == 'asc' ? 'desc' : 'asc'));
            echo ' ('. $multisort['libelle'] . ')';
        } else {
            echo link_to('Libellé', 'Produits/list?sort=libelle&type=asc'); 
        }
        ?>
    </th>

'Produits' is the module name, 'libelle' is the field to sort.


Notes:

The 'reset button' of filters also initialize the multi-sort. The sort is made from the first field clicked to the last. That means, if you want a different primary sort you will have to use the reset filter button before.

PS: Obviously, this modification can be easly integrated in your backoffice theme, note that the default sort criteria set in the 'generator.yml' is used in the addSortCriteria function (from line // default sort)

COil :)

by Loïc Vernet on 2006-10-17, tagged admin  generator  multi  sort 

Comments on this snippet

gravatar icon
#1 William Duan on 2006-12-01 at 03:03

_list_th_tabular.php is in cache, every time when propel-init-admin, it is rebuilt. How to deal with this?

gravatar icon
#2 Loïc Vernet on 2006-12-16 at 06:58

William you are not supposed to use the propel-init-admin several times, only once is enough. After you need to parameter the config/generator.yml file for your module. This is a normal behaviour, the cached files are always regenerated, you must copy the wanted file from the cache to your module (keeping the file struture, in /template in this case) in order to be able to customize the file.

gravatar icon
#3 Leon van der Ree on 2007-08-07 at 03:48

Thanks Loïc for your example

I think I noticed a type at addSortCriteria (the '&' should be removed before the argument).

Also in the table-header I added two variables, to make reproduction easier:

  <th id="sf_admin_list_th_title">
    <?php $column = 'name'; ?>
    <?php $title = 'Name'; ?>
      <?php if (isset($multisort[$column])): ?>
        <?php echo link_to(__($title), 'module/list?sort='.$column.'&type='.($multisort[$column] == 'asc' ? 'desc' : 'asc')) ?>
        (<?php echo $multisort[$column] ?>)
      <?php else: ?>
        <?php echo link_to(__($title), 'module/list?sort='.$column.'&type=asc') ?>
      <?php endif; ?>
    </th>

Maybe I later on even change the generator, but I need some more time for that, which I don't have right now.

gravatar icon
#4 Leon van der Ree on 2007-08-07 at 05:01

Another addition to your post: I've added the option to disable the sorting on a column by clicking on the header for a third time.

this way you get asc->desc->none->asc->desc->none->etc.... Which I think is nicer than only being able to use the reset button from the filter. One thing I haven't figured out though is, if it would be nicer to keep the disabled sorting of the column in memory or not. That means first you have selected to sort on ColumnA (asc) then on columnB (asc) then on ColumnA again (desc). If you would now again press ColumnA (none) ColumnB will now be the first column to sort on if you again press ColumnA.

the code for the table-header is now:

  <th id="sf_admin_list_th_sortorder">
    <?php $column = 'name'; ?>
    <?php $title = 'Name'; ?>
    <?php if (isset($multisort[$column])): ?>
      <?php if ($multisort[$column] == 'asc') $type = 'desc'; elseif ($multisort[$column] == 'desc') $type = 'none'; else $type = 'asc'; ?>
      <?php echo link_to(__($title), 'module/list?sort='.$column.'&type='.$type); ?>
      (<?php echo $multisort[$column] ?>)
    <?php else: ?>
      <?php echo link_to(__($title), 'module/list?sort='.$column.'&type=asc') ?>
    <?php endif; ?>
  </th>

the code for addSortCriteria is now:

    protected function addSortCriteria($c)
    { 
    $multisort = $this->getUser()->getAttributeHolder()->getAll('sf_admin/applicationmodule/sort');
 
        if ($multisort) 
        {
            foreach($multisort as $sort_column => $sort_type) 
            {
                $column = $sort_column;
                $sort_column = Propel::getDB($c->getDbName())->quoteIdentifier($sort_column);
                if ($sort_type == 'asc') {
                    $c->addAscendingOrderByColumn($sort_column);
                }
                elseif ($sort_type == 'desc') 
                {
                    $c->addDescendingOrderByColumn($sort_column);
                }
                elseif ($sort_type == 'none') 
                {
                    //remove column from sort-group
                    $this->getUser()->getAttributeHolder()->remove($column,'sf_admin/applicationmodule/sort');
                }
 
            }
 
        } else {
            //if no sortorder set, here is the place to set a default sort 
 
        }
    }

An even nicer solution can be to keep the switching between asc->desc->asc->desc... and add an option/link in the header to remove the sorting of this column (by adding an X or something)

like E.G.

  <th id="sf_admin_list_th_title">
    <?php $column = 'title'; ?>
    <?php $title = 'Title'; ?>
    <?php if (isset($multisort[$column])): ?>
      <?php echo link_to(__($title), 'module/list?sort='.$column.'&type='.($multisort[$column] == 'asc' ? 'desc' : 'asc')) ?>
      (<?php echo $multisort[$column] ?>)
      <?php echo link_to(__("X"), 'module/list?sort='.$column.'&type=none'); ?>
    <?php else: ?>
      <?php echo link_to(__($title), 'module/list?sort='.$column.'&type=asc') ?>
    <?php endif; ?>
  </th>

and maybe even the 'X' can get replaced by his location in the multisort-array...

gravatar icon
#5 excessive demon on 2007-08-13 at 02:29

Nice to see this works again ;)

I've created an even further improved version which you can find in tract at: http://trac.symfony-project.com/trac/ticket/2092

You now only have to define the option multisort: true in your generator.yml file under list.

gravatar icon
#6 Loïc Vernet on 2007-12-20 at 12:02

Good job :)