Quantcast
Channel: Live News for Yii Framework
Viewing all articles
Browse latest Browse all 3378

[Wiki] How to use Listbox and CheckboxList

$
0
0

Scenario

Assume we want to create a page that gathers a user's favorite foods.

There are many users, and many foods too. And we define users' favorite foods using a junction table.

Models

Here are 4 models involved in the page we want to create.

  1. User extends ActiveRecord (representing user table)
    • id
    • name
    • ... etc.
  2. Food extends ActiveRecord (representing food table)
    • id
    • name
    • ... etc.
  3. FavoriteFood extends ActiveRecord (representing user_food junction table)
    • user_id
    • food_id
  4. UserFavorites extends Model
    • user_id
    • food_ids

The first 3 models are ActiveRecords. You can create them easily with the help of Gii.

But the last one is not an ActiveRecord. It doesn't have a table that it represents. We use it for the form of the page. We have to create it on our own.

Note that 'food_ids' attribute is an array of food ids.

Model for the Form

class UserFavorites extends Model
{
    /**
     * @var integer user ID
     */
    $user_id;
 
    /**
     * @var array IDs of the favorite foods
     */
    $food_ids = [];
 
    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return [
            ['user_id', 'required'],
            ['user_id', 'exist', 'targetClass' => User::className(), 'targetAttribute' => 'id'],
            // each food_id must exist in food table (*1)
            ['food_ids', 'each', 'rule' => [
                'exist', 'targetClass' => Food::className(), 'targetAttribute' => 'id'
            ]],
        ];
    }
 
    /**
     * @return array customized attribute labels
     */
    public function attributeLabels()
    {
        return [
            'user_id' => 'User',
            'food_ids' => 'Favorite Foods',
        ];
    }
 
    /**
     * load the user's favorite foods (*2)
     */
    public function loadFavorites()
    {
        $this->food_ids = [];
        $favfoods = FavoriteFood::find()->where(['user_id' => $this->user_id])->all();
        foreach($favfoods as $ff) {
            $this->food_ids[] = $ff->food_id;
        }
    }
 
    /**
     * save the user's favorite foods (*3)
     */
    public function saveFavorites()
    {
        /* clear the favorite foods of the user before saving */
        FavoriteFood::deleteAll(['user_id' => $this->user_id]);
        if (is_array($this->food_ids)) {
            foreach($this->food_ids as $food_id) {
                $ff = new FavoriteFood();
                $ff->user_id = $this->user_id;
                $ff->food_id = $food_id;
                $ff->save();
            }
        }
        /* Be careful, $this->food_ids can be empty */
    }
 
    /**
     * Get all the available foods (*4)
     * @return array available foods
     */
    public static function getAvailableFoods()
    {
        $foods = Food::find()->order('name')->asArray()->all();
        $items = ArrayHelper::map($foods, 'id', 'name');
        return $items;
    }
}

(*1) In the rules for the validation, we use EachValidator to validate the array of food_ids attribute.

(*2) loadFavorites method loads the IDs of the user's favorite foods into this model instance.

(*3) saveFavorites method saves the user's favorite foods specified in food_ids attribute.

(*4) getAvailableFoods method is an static utility function to get the list of available foods. In the returned array, the keys are 'id' and the values are 'name' of the foods.

Controller

Since we have already implemented all the necessary logic in UserFavorites model, the controller action can be as simple as the following:

/**
 * Gathers the favorite foods of the specified user
 * @param integer $user_id the user's ID
 */
public function actionFavoriteFood($user_id)
{
    $model = new UserFavorites();
    $model->user_id = $user_id;
 
    if ($model->load(Yii::$app->request->post()) {
        if ($model->validate()) {
            $model->saveFavorites();
            return $this->redirect(['index']);
        }
    }
    $model->loadFavorites();
    $items = UserFavorites::getAvailableFoods();
    return $this->render('favorite', [
        'model' => $model,
        'items' => $items,
    ]);
}

View

In the view script, we can use a listBox with multiple selection, or a checkboxList to select the favorite foods.

<?php $form = ActiveForm::begin([
    'id' => 'favorite-form',
    'enableAjaxValidation' => false,
]); ?>
 
<?= Html::activeHiddenInput($model, 'user_id') ?>
 
<?= $form->field($model, 'food_ids')
    ->listBox($items, ['multiple' => true])
    /* or, you may use a checkbox list instead */
    /* ->checkboxList($items) */
    ->hint('Select the favorite foods.');
?>
 
<div class="form-group">
    <?= Html::submitButton('Update', [
        'class' => 'btn btn-primary'
    ]) ?>
</div>
 
<?php ActiveForm::end(); ?>

Viewing all articles
Browse latest Browse all 3378

Latest Images

Trending Articles



Latest Images