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:
- Get the code from github https://github.com/xavierjohn/yii-tabularinput
- Create a MySQL schema called classroom
- Run the migration to create the tables and some sample courses
./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/
@Xavier John
good work :-)
@Xavier John
How to limit row ??
Excellent
Very informative and working solution !!
@Xavier John
sir i follow your tutorial, it works on my system but when i put time picker the picker is not working.. here is my code in my time picker.
<?php $this->widget('application.extensions.jui_timepicker.JTimePicker', array(
'model'=>$model, 'attribute'=>"[$id]time", 'options'=>array( 'showPeriod'=>false, 'hours'=>array('starts'=>07, 'ends'=>18), ), 'htmlOptions'=>array('size'=>5,'maxlength'=>5), ));?>
datepicker in partial render
awesome. It works perfectly. But:
I needed a datepicker in the course/form/_studentRow. It just doesn't show up. When I put datepicker in course/_form, it does work.
what is wrong?
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.