Yii 2.0: How to create/update a model with its related items using Listbox or CheckboxList

4 followers

Scenario

Assume we have many categories and many posts.

A post can belongs to multiple categories, and a category can have many posts. We define posts' categories using a junction table.

Now, we want to develop a page to create/update a post with a list box (or a check box list) that enables the user to define the categories of the post.

Models

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

  1. Post extends ActiveRecord (representing post table)
    • id
    • title
    • body
    • ... etc.
  2. Category extends ActiveRecord (representing category table)
    • id
    • name
    • ... etc.
  3. PostCategory extends ActiveRecord (representing post_category junction table)
    • post_id
    • category_id
  4. PostWithCategories extends Post
    • (all the inherited attributes of Post)
    • category_ids

As for the first 3 models, you can create them easily with the help of Gii.

PostWithCategories model

And the last one is extended from Post. It has an additional attribute called 'category_ids' that will be used to handle the categories of the post. Note that 'category_ids' attribute is an array of category ids.

class PostWithCategories extends Post
{
    /**
     * @var array IDs of the categories
     */
    $category_ids = [];
 
    /**
     * @return array the validation rules.
     */
    public function rules()
    {
        return ArrayHelper::merge(parent::rules(), [
            // each category_id must exist in category table (*1)
            ['category_ids', 'each', 'rule' => [
                    'exist', 'targetClass' => Category::className(), 'targetAttribute' => 'id'
                ]
            ],
        ]);
    }
 
    /**
     * @return array customized attribute labels
     */
    public function attributeLabels()
    {
        return ArrayHelper::merge(parent::attributeLabels(), [
            'category_ids' => 'Categories',
        ]);
    }
 
    /**
     * load the post's categories (*2)
     */
    public function loadCategories()
    {
        $this->category_ids = [];
        if (!empty($this->id)) {
            $rows = PostCategory::find()
                ->select(['category_id'])
                ->where(['post_id' => $this->id])
                ->asArray()
                ->all();
            foreach($rows as $row) {
               $this->category_ids[] = $row['category_id'];
            }
        }
    }
 
    /**
     * save the post's categories (*3)
     */
    public function saveCategories()
    {
        /* clear the categories of the post before saving */
        PostCategory::deleteAll(['post_id' => $this->id]);
        if (is_array($this->category_ids)) {
            foreach($this->category_ids as $category_id) {
                $pc = new PostCategory();
                $pc->post_id = $this->post_id;
                $pc->category_id = $category_id;
                $pc->save();
            }
        }
        /* Be careful, $this->category_ids can be empty */
    }
}

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

(*2) loadCategories method loads the IDs of the post's categories into this model instance.

(*3) saveCategories method saves the post's categories specified in category_ids attribute.

Category model

We want to add a small utility method to Category model.

class Category extends ActiveRecord
{
    ...
 
    /**
     * Get all the available categories (*4)
     * @return array available categories
     */
    public static function getAvailableCategories()
    {
        $categories = self::find()->order('name')->asArray()->all();
        $items = ArrayHelper::map($category, 'id', 'name');
        return $items;
    }
}

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

Controller Actions and Views

Since we already have all the necessary logic in models, the controller actions can be as simple as the following examples.

Create action

/**
 * Create Post with its categories
 */
public function actionCreate()
{
    $model = new PostWithCategories();
 
    if ($model->load(Yii::$app->request->post()) {
        if ($model->save()) {
            $model->saveCategories();
            return $this->redirect(['index']);
        }
    }
 
    return $this->render('create', [
        'model' => $model,
        'categories' => Category::getAvailableCategories(),
    ]);
}

create.php view script

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

<?php $form = ActiveForm::begin([
    'id' => 'post-form',
    'enableAjaxValidation' => false,
]); ?>
 
<?= $form->field($model, 'title')->textInput(); ?>
 
<?= $form->field($model, 'body')->textArea(); ?>
 
...
 
<?= $form->field($model, 'category_ids')
    ->listBox($categories, ['multiple' => true])
    /* or, you may use a checkbox list instead */
    /* ->checkboxList($categories) */
    ->hint('Select the categories of the post.');
?>
 
<div class="form-group">
    <?= Html::submitButton('Create', [
        'class' => 'btn btn-primary'
    ]) ?>
</div>
 
<?php ActiveForm::end(); ?>

Update action

It's almost the same with Create action:

/**
 * Update a post with its categories
 * @param integer $id the post's ID
 */
public function actionUpdate($id)
{
    $model = PostWithCategories::findOne($id);
    $model->loadCategories();
 
    if ($model->load(Yii::$app->request->post()) {
        if ($model->save()) {
            $model->saveCategories();
            return $this->redirect(['index']);
        }
    }
 
    return $this->render('update', [
        'model' => $model,
        'categories' => Category::getAvailableCategories(),
    ]);
}

update.php view script

It's different from create.php only in the text of the submit button:

...
    <?= Html::submitButton('Update', [
        'class' => 'btn btn-primary'
    ]) ?>
...

Total 1 comment

#19879 report it
faustjonson at 2016/06/15 07:52am
Listbox

I use view script, and its me help writers

Leave a comment

Please to leave your comment.

Write new article