1
votes

I am building an eLearning site in CakePHP that has models: Course, Test, Question, Answer, and others.

Here are my associations:

  • Course hasMany Test
  • Test belongsTo Course
  • Question belongsTo Test
  • Question hasMany Answer
  • Answer belongsTo Question

This model association works fine in most circumstances. For example, the baked CRUD actions for Course know that there are Test(s) that belong to it, Questions know they have associated Answer(s), etc...

But I am trying to write a Test management tool that operates within the TestsController and level of association ends at Question. There are no answers. In other words, the view I'm working on has this by default from the baked TestsController:

Array
(
[Test] => Array
    (
        [id] => 1
        [name] => Test 1
        [course_id] => 1
        [time_limit] => 30
        [max_questions] => 20
        [randomize_order] => 1
        [num_questions] => 2
    )

[Course] => Array
    (
        [id] => 1
        [course_identifier] => TLE1001
        [title] => Child Development I
        [description] => 
    )

[Question] => Array
    (
        [0] => Array
            (
                [id] => 1
                [text] => What is your name?
                [test_id] => 1
                [order] => 0
            )

        [1] => Array
            (
                [id] => 2
                [text] => What is my name?
                [test_id] => 1
                [order] => 0
            )

    )

)

As you can see, the $test array in my view stops at Question, and doesn't pull the associated Answer(s) for the Question(s).

I fixed this problem by changing the bottom of my 'view' action in TestsController:

$test = $this->Test->read(null, $id);
$test['Question'] = $this->Test->Question->find('all'); //I added this line...
$this->set('test', $test);

I added the middle line so it literally replaces all the contents of $test['Question'] manually before it sets the $test array for the view to use.

Here is the result:

Array
(
[Test] => Array
    (
        [id] => 1
        [name] => Test 1
        [course_id] => 1
        [time_limit] => 30
        [max_questions] => 20
        [randomize_order] => 1
        [num_questions] => 2
    )

[Course] => Array
    (
        [id] => 1
        [course_identifier] => TLE1001
        [title] => Child Development I
        [description] => The first years of life offer a window of opportunity for a child’s development. A solid understanding of how children grow and develop is essential to providing a quality care environment.
    )

[Question] => Array
    (
        [0] => Array
            (
                [Question] => Array
                    (
                        [id] => 1
                        [text] => What is your name?
                        [test_id] => 1
                        [order] => 0
                    )

                [Test] => Array
                    (
                        [id] => 1
                        [name] => Test 1
                        [course_id] => 1
                        [time_limit] => 30
                        [max_questions] => 20
                        [randomize_order] => 1
                        [num_questions] => 2
                    )

                [Answer] => Array
                    (
                        [0] => Array
                            (
                                [id] => 1
                                [question_id] => 1
                                [text] => My name is what I say it is.
                                [correct] => 1
                            )

                        [1] => Array
                            (
                                [id] => 2
                                [question_id] => 1
                                [text] => My name is Earl.
                                [correct] => 
                            )

                    )

            )

        [1] => Array
            (
                [Question] => Array
                    (
                        [id] => 2
                        [text] => What is my name?
                        [test_id] => 1
                        [order] => 0
                    )

                [Test] => Array
                    (
                        [id] => 1
                        [name] => Test 1
                        [course_id] => 1
                        [time_limit] => 30
                        [max_questions] => 20
                        [randomize_order] => 1
                        [num_questions] => 2
                    )

                [Answer] => Array
                    (
                        [0] => Array
                            (
                                [id] => 3
                                [question_id] => 2
                                [text] => None of your business
                                [correct] => 1
                            )

                        [1] => Array
                            (
                                [id] => 4
                                [question_id] => 2
                                [text] => Jim
                                [correct] => 
                            )

                    )

            )

    )

)

That works for what I have to accomplish, but it seems ugly to me. As you can see, the Answer(s) get loaded but I also have duplicate info now because when it loads the Question Model it pulls in the associated Test. And I'm pretty sure this doesn't jive with CakePHP conventions...

Is there a better way to do this? Does CakePHP only build associated data array's for model association up to a certain level then stop?

P.S. I tried to add:

var $uses = array('Test', 'Question', 'Answer');

...to the top of my TestsController but it did not fix the problem and seemed to have no effect.

There must be a better way to get CakePHP to load ALL the associated data models AND THEIR associations as well...

2

2 Answers

2
votes

Use CakePHP's Containable Behaviour: http://book.cakephp.org/1.3/view/1323/Containable

I think for your specific case, the query would look like:

$this->Test->find('all', array(
    'contain'=>array('Question')
    )
);

You could also use conditions, ordering, etc.

1
votes

I used the recursive property which seemed to do it.

$test = $this->Test->find('all', array('recursive' => 2));

Tells CakePHP to load another level of association, so the result includes not only the Question(s) but also their associated Answer(s).