CakePHP:如何对嵌套的HABTM模型使用Containable

时间:2013-06-05 16:12:16

标签: php cakephp cakephp-2.0 cakephp-2.1

我正在尝试使用Containable返回一个包含多个相关模型(以及那些数据数组)的模型。模型处理测试结果。以下是模型:

型号:

Question : has and belongs to many Category
Category : has and belongs to many Question
Attempt : has many AttemptedQuestions
AttemptedQuestion : belongs to Question

我想返回一个Attempt,其中包含所有的AttemptedQuestions和他们的相应的Quesions + Category

所以基本上,关系映射如下:

Attempt => AttemptedQuestion(s) => Question => Category

我怀疑由于HABTM关系,Cake更愿意返回:

Attempt => AttemptedQuestion(s) => array( Question, Category )< - 类别不包含在问题数组中,而是包含在姐妹中。这也很好。

目前我无法让分类显示出来。这是我在控制器中所做的事情(这不会导致类别出现在结果中):

$this->Attempt->contain(array('AttemptedQuestion' => array('Question'=>'Category'))); 
$attempt_to_be_graded = $this->Attempt->findById( $attempt_id );

我做错了什么?

更新 这是基于@nunser答案的修订版。这也行不通。

$this->Attempt->contain(array('AttemptedQuestion' => array('Question'=>array('Question'=>array('Category') )))); 
$attempt_to_be_graded = $this->Attempt->findById($attempt_id );

这是返回的数据:

array(
    'Attempt' => array(
        'id' => '39',
        ...
    ),
    'AttemptedQuestion' => array(
        (int) 0 => array(
            'id' => '189',
            ...,
            'Question' => array(
                'id' => '165',
                ...
            )
        ),
        (int) 1 => array(
            'id' => '188',
            ...,
            'Question' => array(
                'id' => '164',
                ...
            )
        )

    )
)

更新2

我仍然在努力解决这个问题,但我认为我的关联必须正确,因为以下内容会按预期返回所有类别的列表:

$categories = $this->Attempt->AttemptedQuestion->Question->Category->find('all');

更新3

通过从代码中的各个点测试$this->Question->find('first')的结果,我缩小了这个问题的范围。在此之后,结果似乎是预期的结果:$test = $this->Test->findById($test_id);

以下代码演示:

    $this->loadModel('Question');       
    $test = $this->Question->find('first');
    debug($test);
//THESE RESULTS INCLUDE Category DATE

    $test = $this->Test->findById($test_id);

    $this->loadModel('Question');       
    $test = $this->Question->find('first');
    debug($test);
    exit;
//THESE RESULTS DO NOT INCLUDE Category DATE

因此,由于我完全不理解的原因,干预Test->find()似乎阻止了类别数据的出现。很奇怪,是吗?

4 个答案:

答案 0 :(得分:1)

有时,在我这边最好使用binModel嵌套而不是行为,因为更可配置,请检查此链接,你可能可以帮助你解决问题

CakePHP - Building a Complex Query

cakephp join more than 2 tables at once in one join table

答案 1 :(得分:1)

HABTM头痛

这些在Cake中难以管理。我开始做的是“hasMany Threw”,即described in the book here

优点是可以更好地控制查找查询。它需要额外添加Model,但这是值得的。 CakePHP中HABTM的自动处理通常很难理解。使用关联来明确定义关系更灵活。

循环参考

您的模型目前设置了循环引用,这对于HABTM来说可能会有问题。

Question : has and belongs to many Category
Category : has and belongs to many Question

在Cake中很难在两个模型上创建HABTM。通常这只在其中一个上完成,我选择Question。使用“hasMany Threw”会更容易。

HasMany投掷

如果我们使用“hasMany Threw”,那么可容纳的规则就会变得容易解释。

Question : hasMany QuestionCategory
Category : hasMany QuestionCategory
QuestionCategory : belongs to Question, belongs to Category.
Attempt : belongs to Question

现在,我删除了AttamptedQuestion。我不确定你要模拟的是什么,但如果一个人可以为每个问题创建多次尝试,那么就不需要了。只需创建一个以上的尝试记录。

查找所有记录以进行尝试

要使用Attempt查找Containable的所有相关记录,搜索结果如下:

$this->Attempt->find('first',array(
    'conditions'=>array('Attempt.id'=>$attempt_id),
    'contain'=>array(
        'Question'=>array(
            'QuestionCategory'=>array(
                'Category'=>array()
            )
        )
    ));

您仍然可以添加AttemptedQuestion模型,并在Question之前包含它。

带关联的不明确字段名

你应养成在条件中使用Attempt.id而不仅仅是id的习惯。使用关联时,SQL结果中通常会有重复的字段名。您需要澄清您所引用的内容,因为如果使用Question.id,您还可以在Attempt中将JOIN作为条件。所以id本身就是模棱两可的。

答案 2 :(得分:1)

提供有关触发问题的新信息。

$this->loadModel('Question');       
$test = $this->Question->find('first');
debug($test);
//THESE RESULTS INCLUDE Category DATE

$test = $this->Test->findById($test_id);

$this->loadModel('Question');       
$test = $this->Question->find('first');
debug($test);
exit;
//THESE RESULTS DO NOT INCLUDE Category DATE

在上面的场景中,findById魔术函数(我称之为它)调用较低级别的数据源驱动程序来执行查询。

DboSource.php中,它不使用Containable行为来控制递归,并更改recursive中的Model属性。我认为这会导致Containable行为的麻烦。

您可以在recursive之后将findById重置为默认值来解决此问题。

$test = $this->Test->findById($test_id);
$this->Test->recursive = 1;

$this->loadModel('Question');       
$test = $this->Question->find('first');
debug($test);
exit;

我猜这会解决问题,但你必须记住在使用findById或任何其他魔法功能后重置它。

如果它是可重现的,应该向Cake开发人员报告错误。

修改

递归默认值为1而不是0。

答案 3 :(得分:0)

尝试

$this->Attempt->find('first', array('conditions'=>array('id'=>$attempt_id),
                                'contain'=>array('AttemptedQuestion'=> 
                                               array('Question' => 
                                                  array('Category')
                                               )
                                )));