Update related models of a model using listBox and checkBoxList in the form

Suppose you want to have a list in Category with its related products and you want to updates the list (removing or insert product items) This wiki show us how to do that

schema: Model Product (id,name,price,category_id) Model Category (id,name)

So in this case a product has only one category and category has many products

In the Category.php model

public function relations() {
        return array(
            ...
            'relatedProducts' => array(self::HAS_MANY, 'Product', 'category_id'),
        );
}
 
 public function rules() {
        return array(
            ...
            array('product_ids', 'type', 'type' => 'array', 'allowEmpty' => true),
        );
 }
    
 
  public $product_stored_ids = array();
  public $product_ids = array();

  public function afterFind() {

        $this->product_ids = [];
        foreach ($this->relatedProducts as $r) {
            $this->product_ids[] = $r->id;
        }

        $this->product_stored_ids = $this->product_ids;

        parent::afterFind();
   }

    protected function afterSave() {


        if (!$this->product_ids) //if nothing selected set it as an empty array
            $this->product_ids = array();

        //save the new selected ids that are not exist in the stored ids
        $ids_to_update = array_diff($this->product_ids, $this->product_stored_ids);

        foreach ($ids_to_update as $uid) {
            $p = Product::model()->findByPk($uid);
            if ($p) {
                $p->category_id = $this->id;
                $p->save();
            }
        }


        //remove the stored ids that are not exist in the selected ids
        $ids_to_remove = array_diff($this->product_stored_ids, $this->product_ids);


        foreach ($ids_to_remove as $did) {            
            if ($p = Product::model()->findByPk()) {
                $p->category_id = NULL;
                $p->save();
            }
        }

        parent::afterSave();
    }

view/category/_form.php

<?php
        $form = $this->beginWidget('CActiveForm', array(
            'id' => 'category-form',
            'enableAjaxValidation' => false,
        ));
    ?>
 
 
    <h2>Products in category</h2>

    <?php
        $data = CHtml::listData(Product::model()->findAll(), 'id', 'Title');
        echo $form->listBox($model, 'product_ids', $data, array('multiple' => 'true'));
        //or echo $form->checkBoxList($model, 'product_ids', $data);
    ?>

    <div class="row buttons">
        <?php echo CHtml::submitButton($model->isNewRecord ? 'Create' : 'Save'); ?>
    </div>

    <?php $this->endWidget(); ?>