How to get multichecked checkboxes data in update action and save it in junction table

Hi. I have two models (Video, Category). In my video form i am fetching the categories (from Category model) and show them as checkboxes (note that: there is no categories field inside video table). each video can have single or multiple categories. I need to save the video categories (which are to be selected inside video form) inside a junction table (video_categories). I will save the categories using afterSave() inside Video model.
My main problem is that how can i fetch my checked categories from video form inside updateAction() of VideoController, and assign them to some public member (of Video model) and then use that member in afterSave() to save the categories inside that junction table (video_categories).
One more thing is that I want to save the categories inside updateAction() only. In createAction() i am saving just the video file. all other details about video needs to be saved inside updateAction().

This is how I displayed Categories inside video form.

<ul class="catgss_list">
<?php 
$category = common\models\Category::find()->all(); 
foreach($category as $cat):
?>
<li>
    <div class="chekbox-lg">
        <label>
            <input type="checkbox" name="category[]" value="<?php echo $cat->id ?>">
            <b class="checkmark"></b>
            <span><?= $cat->category_name ?></span>
        </label>
    </div>
</li>
<?php endforeach; ?>
</ul>

this is how my form looks like Screenshot-1 — ImgBB

1 Like

i have mostly solved my problem. what I did is written below:-
my Video model

public $categories;// i declare a public attribute. 
public function afterSave($insert, $changedAttributes) {
  parent::afterSave($insert, $changedAttributes);
  if (isset($_POST['Video']['categories'])) {
      $this->deleteExistingCategories();
      $this->saveVideoCategories($_POST['Video']['categories']);
  }
}

public function deleteExistingCategories() {
  $deleteRecord = VideoCategory::findAll(['video_id' => $this->video_id]);
  foreach ($deleteRecord as $record) {
      $record->delete();
  }
}

public function saveVideoCategories($categoriesList) {
  foreach ($categoriesList as $category) {
      $videoCategory = new VideoCategory();
      $videoCategory->video_id = $this->video_id;
      $videoCategory->category_id = $category;
      $videoCategory->save();
  }
}

my video form code

<ul class="catgss_list">
<?php
$checkedCategories = VideoCategory::findAll(['video_id' => $model->video_id]);
if (isset($checkedCategories)) {
    foreach ($checkedCategories as $selectedCategory) {
        $savedCategories[] = $selectedCategory->category_id;
    }
}
$category = Category::find()->all();
foreach ($category as $cat):
?>
<li>
    <div class="chekbox-lg">
        <label>
            <input type="checkbox" name="Video[categories][]" value="<?php echo $cat->id ?>" 
            <?php
            if (isset($savedCategories)) {
                $isCategory = in_array($cat->id, $savedCategories);
                if (isset($isCategory) && $isCategory == true) {
                    echo "checked";
                }
            }
            ?>
                   >
            <b class="checkmark"></b>
            <span><?php echo $cat->category_name ?></span>
        </label>
    </div>
</li>
<?php endforeach; ?>
</ul>

now there is only one issue.
when unchecked all the categories then it doesn’t perform the delete operation in afterSave(). when unchecked all other categories but keep one checked then it perform the delete operation in afterSave(). when I have one category checked and then also un-check it then also it dosn’t perform the delete operation of afterSave(). in short we have to keep at least one category checked but we want no categories to be checked if user uncheck all categories. I am trying to resolve this, but still not found any way.
Any help would be highly recommended. also if any suggestion regarding my current code are also welcome.

1 Like

This should work.

public function afterSave($insert, $changedAttributes) {
  parent::afterSave($insert, $changedAttributes);
  $this->deleteExistingCategories(); // here
  if (isset($_POST['Video']['categories'])) {
      // $this->deleteExistingCategories();  // not here
      $this->saveVideoCategories($_POST['Video']['categories']);
  }
}

And, deleteExistingCategories() could be optimized:

public function deleteExistingCategories() {
//  $deleteRecord = VideoCategory::findAll(['video_id' => $this->video_id]);
//  foreach ($deleteRecord as $record) {
//      $record->delete();
//  }
    VideoCategory::deleteAll(['video_id' => $this->video_id]);
}
1 Like

Thanks a lot. I solved my problem with your help. :slight_smile:

1 Like

Hi @softark I have an another problem related to this.
In the same project i can have multiple users. and each user will have videos and also videos will have videos_categories.
just like in Video model afterDelete() function
I want to implement afterDelete() in User model too when a certian user is deleted, and i have done it.
but in the below code

public function afterDelete() {
        parent::afterDelete();
        $allVideos = Video::findAll(['created_by' => $this->id]);
        foreach ($allVideos as $video){
            $video->delete();
        }
        if($this->has_picture == 1){
            $user_thumbnail = Yii::getAlias('@frontend/web/storage/user_profile/'.$this->username.'.jpg');
            unlink($user_thumbnail);
        }
    }

when i replace this

$allVideos = Video::findAll(['created_by' => $this->id]);
 foreach ($allVideos as $video){
  $video->delete();
}

with just this

Video::deleteAll(['created_by' => $this->id]);

then it just delete the videos from video table (in database). it doesn’t perform the Video model afterDelete() functionality (i.e. it doesn’t delete record of videos_categories table, also it doesn’t delete the video and video_thumbail image files from the directory).

1 Like

deleteAll() is a static method and it doesn’t perform afterDelete() that is meant for an individual ActiveRecord object.

A related record of videos_categories table could be deleted by the foreign key constraint of CASCADE_DELETE, but the thumbnail image file can’t be deleted by deleteAll().

So, you have to use delete() in this case.

2 Likes