Tabular Input, Validating and Saving related models

You are viewing revision #2 of this wiki article.
This version may not be up to date with the latest version.
You may want to view the differences to the latest version or see the changes made in this revision.

« previous (#1)next (#3) »

This tutorial shows how to use a model to save and validate related (MANY_MANY) models.

In this tutorial you will find

  • Validating model as a whole including its related models
  • Collecting data for the model and its related models (tabular input)
  • Displaying errors from the related models.
  • Saving the primary model and its related models.
  • Natural association ( $model->students = array($studentOne, $studentTwo); )

Usage:

./protected/yiic migrate

./protected/yiic createCourses

Run the site.

Details:

Validation of the Course model is done by over loading the validate method. This method can be used to validate the model as a whole, like prevent students with the same name.

public function validate($attributes = null, $clearErrors = true) {
        $isValid = parent::validate($attributes, $clearErrors);
        foreach ($this->students as $student) {
            if ($student->validate() == false) {
                $isValid = false;
                $this->addErrors($student->getErrors());
            }
        }
        return $isValid;
    }

Next we overload the save method to save all related models.

public function save($runValidation = true, $attributes = null) {
    if ($runValidation) {
        if ($this->validate($attributes) == false)
            return false;
    }
    foreach ($this->students as $student) {
        $student->save(false);
    }
    return parent::save(false, $attributes);
}

Now I can create a course model and its related students.

Snip from protected/tests/unit/CourseTest.php

$courseName = 'testCourse';
    $studentOne = new Student();
    $studentOne->first_name = 'Firsty';
    $studentOne->last_name = 'Test';
    $studentTwo = new Student();
    $studentTwo->first_name = 'Secondy';
    $studentTwo->last_name = 'Test';

    $model = new Course();
    $model->name = $courseName;
    $model->description = 'This course was created for testing purpose';
    $model->students = array($studentOne, $studentTwo);
    if ($model->save() == true) {
        echo 'Successfully created ' . $model->name . PHP_EOL;
    } else {
        echo 'Failed to create ' . $model->name . PHP_EOL;
        print_r($model->Errors);
    }

EAdvancedArBehavior is used to update the relationship table. http://www.yiiframework.com/extension/eadvancedarbehavior/

To collect tabular input of students for a course I go through the Student collection as

if (isset($_POST['Course'])) {
    $model->attributes = $_POST['Course'];
    $model->students = CourseController::assignStudents($model, $_POST['Student']);

CourseController::assignStudents is defined as

public static function assignStudents($model, $items_posted) {
        $students = array();
        foreach ($items_posted as $item_post) {
            $student = CourseController::findStudent($model, $item_post['id']);
            if (is_null($student)) {
                $student = new Student();
            }
            unset($item_post['id']); // Remove primary key
            $student->attributes = $item_post;
            array_push($students, $student);
        }
        return $students;
    }

    public static function findStudent($model, $id) {
        $student = null;
        foreach ($model->students as $s) {
            if ($s->id == $id) {
                $student = $s;
            }
        }
        return $student;
    }

I got the idea to solve the tabular input from http://www.yiiframework.com/extension/ztabularinputmanager/