0
votes

I have 3 models: Student, Course and StudentCourse. Course 'hasAndBelongsToMany' Student, Student 'hasMany' Course, and StudentCourse 'belongsTo' Student and Course. Before a student can signup for a course, I need to check a few things (ie: is the course full, has that student taken that course in the past, etc). I can handle the logic inside of the function, but which model should I place that function under? And, how should it be called? One way I thought of was:

// Student Model
public function canSignupForCourse($studentId, $courseId) {
    // is the course full?
    // have they signed up before, etc
    // return either true or false
}

// Could it then be called anywhere as:
if($this->Student->canSignupForCourse($studentId, $courseId)) {
    // etc
}

Or, is there a better/easier way to do it (and, do I need to send both the studentid and courseid each time)?

2

2 Answers

2
votes

I think the best thing to do is to try to implement these restrictions as validation rule in the model.

According to your description, applying a student for a course is done by creating a new StudentCourse, so that's where you should try to fit the validation rules, for example:

// StudentCourse.php

$validate = array(
    'course_id' => array(
         'rule' => array('maxStudents', 30),
         'required' => true,
         'on' => 'create'
    )
)

function maxStudents($check, $max) {
    $count = $this->find('count', array(
        'conditions' => array('course_id' => $check['course_id']),
        'contain' => false
    ));
    return $count < $max;
}
0
votes

I'd first check out the example in the manual here: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany-through-the-join-model

This should convince you that you should probably make Student 'hasAndBelongsToMany' course as well (since Course has student but student doesnt belongto course in your model relationships)

You can then define that relationship model as something like CourseMembership (as in the example link above)

I would then put canSignupForCourse function in that model. However I'd probably split this function up into a few separate ones, like courseNotFull and courseNotTakenBefore

I would then put these functions into the model's validate object like so:

public $validate = array( 
    'course_id' => array(
        'courseNotFull' => array( 
            'rule' => array('courseNotFull'), 
            'message' => "Course is full", 
        ), 
        'courseNotTakenBefore' => array(
           'rule' => array('courseNotTakenBefore'), 
            'message' => "Student has taken course before", 
        )
    )
); 

And define the model functions like this:

function courseNotFull() {
    $this->Course->id = $this->data[$this->alias]['course_id'];
    $course = $this->Course->read();

    return $course['Course']['isFull'];
}

function courseTakenBefore() {
    $this->Student->id = $this->data[$this->alias]['student_id'];
    $this->Course->id = $this->data[$this->alias]['course_id'];

    $course = $this->Student->Course->findById($this->Course->id);

    return $course;
}

Now whenever you try to save or validate() CourseMembership, the validate will return a descriptive error message if it is unsuccessful.