Ordering Models by Weight with CJuiSortable

  1. Setting up the Model
  2. Setting up the Views
  3. Setting up the Controller
  4. Implementation

In this tutorial we will be setting up a content type to be ordered by weight using the JUI Sortable plugin.

Note: This is probably not suitable for large data sets, as each model is updated on submission.

Tutorial Requirements

  • Database table setup with at least the fields id, title, and weight.
  • A content type created with the Yiic shell crud command.

Setting up the Model

If you haven't done so already, we need to validate the weight, so add the following code to your rules() function:

public function rules()
{
    return array(
        array('weight', 'numerical'),
    );
}

Setting up the Views

First we need to set up the weight field as a drop down list to keep our bases covered when editing individual models, so modify the _form.php view as follows (changing YourClass accordingly):

<div class="row">
    <?php echo CHtml::activeLabelEx($model, 'weight'); ?>
    <?php
        // Set the number of models to be the heaviest weight
        // and format into a dropDownList-friendly array
        $models = YourClass::model()->findAll();

        for ($i = 0; $i < sizeof($models); $i++) { $weights[$i] = $i; }

        echo CHtml::activeDropDownList($model, 'weight', $weights);
    ?>
    <div class="hint">
        Selecting a lighter weight (e.g. 0) will 
        place the model higher in the list.
    </div>
    <?php echo CHtml::error($model, 'weight'); ?>
</div>

Now we need to create the view in which we actually order the models, so go ahead and duplicate index.php and rename to order.php. Modify the page title and breadcrumbs and whatever else as needed, and replace the CListView widget with:

<?php
    // Organize the dataProvider data into a Zii-friendly array
    $items = CHtml::listData($dataProvider->getData(), 'id', 'title');
    // Implement the JUI Sortable plugin
    $this->widget('zii.widgets.jui.CJuiSortable', array(
        'id' => 'orderList',
        'items' => $items,
    ));
    // Add a Submit button to send data to the controller
    echo CHtml::ajaxButton('Submit Changes', '', array(
        'type' => 'POST',
        'data' => array(
            // Turn the Javascript array into a PHP-friendly string
            'Order' => 'js:$("ul.ui-sortable").sortable("toArray").toString()',
        )
    ));
?>

At this point it would also be good to setup menu items in the rest of the views as needed, using this code for example: ~~~ [html]

<?php echo CHtml::link('Order Models', array('order')); ?> ~~~

Setting up the Controller

First we need to give a particular user(s) the permission to order these items. Modify the accessRules() function in your controller to have the action order. For example:

public function accessRules()
{
    return array(
        array('allow',
            'actions' => array('order'),
            'users' => array('admin'),
        ),
   );
}

Now we need to create the action that will handle the ordering. Add the following code somewhere in your controller (changing YourClass accordingly).

/**
 * Handles the ordering of models.
 */
public function actionOrder()
{
    // Handle the POST request data submission
    if (isset($_POST['Order']))
    {
        // Since we converted the Javascript array to a string,
        // convert the string back to a PHP array
        $models = explode(',', $_POST['Order']);

        for ($i = 0; $i < sizeof($models); $i++)
        {
            if ($model = YourClass::model()->findbyPk($models[$i]))
            {
                // Use updateByPK to avoid running model validate
                $model->updateByPk( $models[$i],array("weight"=>$i) );
            }
        }
    }
    // Handle the regular model order view
    else
    {
        $dataProvider = new CActiveDataProvider('YourClass', array(
            'pagination' => false,
            'criteria' => array(
                'order' => 'weight ASC, id DESC',
            ),
        ));

        $this->render('order',array(
            'dataProvider' => $dataProvider,
        ));
    }
}

Implementation

Now whenever you are using a dataProvider, just set the order criteria to:

'criteria' => array(
    'order' => 'weight ASC, id DESC',
),

and you should be good to go!

9 0
10 followers
Viewed: 16 376 times
Version: 1.1
Category: Tutorials
Tags:
Written by: Matt Kelliher
Last updated by: Phil Loaiza
Created on: Mar 11, 2010
Last updated: 10 years ago
Update Article

Revisions

View all history