通过bindModel()w / o递归访问HABTM关系的关联模型

时间:2013-12-03 01:27:17

标签: mysql cakephp model has-and-belongs-to-many cakephp-2.4

坚果壳中的问题

我想通过模型B的控制器中的find()操作从模型A 中检索HABTM 模型B 中的数据,而不依赖于递归。

$this->ModelB->bindModel('hasMany' => array('ModelAsModelBs'));
$this->ModelB->find('all', array('fields' => array('ModelA.*'))); //cond'ts below

我知道bindModel()需要这样做,但我似乎无法访问相关的模型字段(即不仅仅是HABTM表的字段,而是实际关联的模型< / em>)没有多次递归。

在我看来,我也可能从根本上误解了模型关系应该如何互动,设计或检索等等。总之,我认识到我可能没有成功的原因是这个嘿,可能不是我应该做的事情。如果是这样的话,我会同样乐于学习如何更好地做到这一点,因为我经常处理非常复杂的模型关系(我主要为学术界和在线课程材料/远程研究进行网络开发)。

具体示例/实际情况

数据库有这些表,你想要的是id:

  • 用户
  • 课程
  • 模块
  • UsersCourses
  • UsersModules
  • CoursesModules

我正在尝试执行的模型查询发生在Users控制器中,如下所示:

class Users extends AppController {
    public function doSomething() {
        $hasOne = array( 'hasOne' => array('CoursesModules','UsersModules'));
        $this->User->Course->Module->bindModel($hasOne);
        $conditions = array('`CoursesModules`.`course_id`' => $courseIds, //this is a known constraint within the app
                    'NOT' => array('`UsersModules`.`module_id` = `CoursesModules`.`module_id`' ));
        $options = array( 'fields' => array('Module', 'Course.*'), // Note the attempt to get Course model information
                  'conditions' => $conditions,
                  'recursive' => 0);
        $modules = $this->User->Course->Module->find('all', $options);
        $this->set(compact('modules'));
    }
}

此查询的结果为:

错误: SQLSTATE [42S02]:未找到基表或视图:1051未知表'课程'

但我也无法使用bindModel()CoursesModules联系起来。这很奇怪,因为关联路径是Users->Courses->Modules。我可以将递归提升一个档次,但它会导致需要大量unbind()的各种地狱,并且还会提取完全荒谬的数据量。

第二个奇怪的是,如果我从字段列表中删除Course.*,则上述查询会执行,但不会像我期望的那样工作;我认为这正确地要求 CoursesModules 中列出的 UsersModules 中的所有模块。这些数据确实存在于我的记录中,但未被此检索。

我意识到我可以从course_ids获得CoursesModules,然后再做另一个找到获得课程模型数据,但这是a)不是非常像蛋糕和b)痛苦,因为我'我非常感谢在渲染的视图文件中访问$modules['Module']['Course']

有人能指出我在正确的方向吗?或者,哈哈,上帝保佑,帮我建立这个MySQL查询(我对MySQL连接都赞不绝口)?真的很感激,谢谢!

更新

@Kai:为了建立关系,我设置了桌子并烤好了。或许值得注意的是,我对MySQL有一个相当基本的把握,并且通常通过PhpMyAdmin完成所有工作。至于生成初始Cake文件,我使用cake bake all,然后在我去的时候修改了一些东西。表的SQL和各个模型的$hasAndBelongsToMany数组将在最后发布。

至于我选择hasOne的原因......我也假设hasMany;使用此关系包括从我绑定的表中生成“未找到列”错误(与哪个列无关)。同时,hasOne明显错误的选择在某种程度上起了作用。

最后,我怀疑这个containable behavior业务可能就像我追求的那样,但我真的不明白。尽我所能,这是这些模型的上下文以及我正在尝试执行的各种查询:

我正在为大学教师建立一个程序,基本上让教授有一些在线课程。但是课程(即模块)可能在不同的课程(即课程)之间共享,学生可能在任何或所有课程中。另一个限制是学生可以选择她将在给定课程中执行哪些模块 - 教授可以提供五个模块,他们必须完成任何三个模块。因此,当学生登录时,我需要能够在他们所在课程的背景下检索他们尚未完成的模块。

我必须做出大量类似的查询,或多或少具有这种性质。按照目前的情况,我可以通过loadModel()的各种用途实现所有这些(并且因为这是在截止日期之前完成的),执行更简单的$this->Model->find(),通过一些{{1}对结果进行排序逻辑,冲洗重复。 ,除了烦恼之外,我还担心它不可扩展,因为我的处理过度,最后......我讨厌做错事,哈哈。我知道CakePHP可以处理我想问我的数据的问题,我只是不知道如何问他们(和/或设置数据以便可以询问这些问题)。

foreach

模型关联

注意:这不是全面的 - hasOne,hasMany,belongsTo等。为了节省空间,省略了;如果需要,我可以发布整个模型。

CREATE TABLE IF NOT EXISTS `modules` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `domain_id` int(11) NOT NULL,
  `subject_id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `description` text NOT NULL,
  `passing_score` int(11) NOT NULL,
  `max_attempts` int(11) NOT NULL,
  `allow_retry` int(11) NOT NULL,
  `running_score` tinyint(1) NOT NULL,
  `score_privacy` int(11) NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `domain_id` (`domain_id`),
  KEY `subject_id` (`subject_id`)
)  ENGINE=InnoDB  DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `group_id` int(11) NOT NULL,
  `institution_id` int(11) NOT NULL,
  `password` varchar(255) NOT NULL,
  `firstname` varchar(63) NOT NULL,
  `lastname` varchar(63) NOT NULL,
  `email` varchar(255) NOT NULL,
  `studentno` varchar(31) NOT NULL,
  `claimed` tinyint(1) NOT NULL,
  `verified` tinyint(1) NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `group_id` (`group_id`),
  KEY `institution_id` (`institution_id`)
)  ENGINE=InnoDB  DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS `courses` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `institution_id` int(11) NOT NULL,
  `coursecode` varchar(255) NOT NULL,
  `name` varchar(255) NOT NULL,
  `semester` varchar(7) NOT NULL,
  `educator_id` int(11) NOT NULL,
  `year` year(4) NOT NULL,
  `description` text NOT NULL,
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`educator_id`), /* educator is an alias of user in some cases */
  KEY `institution_id` (`institution_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS `users_courses` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `course_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`,`course_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1; 

CREATE TABLE IF NOT EXISTS `users_modules` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `module_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `user_id` (`user_id`,`module_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 

CREATE TABLE IF NOT EXISTS `courses_modules` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `course_id` int(11) NOT NULL,
  `module_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `course_id` (`course_id`,`module_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

2 个答案:

答案 0 :(得分:1)

据我了解,这就是问题;用户通过他们的课程获得可用模块的列表。我们希望找到用户尚未完成的可用模块。换句话说,我们必须找到不在该用户模块中的用户课程模块。

这是我能想到的最佳方式:

$modules = $this->User->Module->find('all', array(
    'joins' => array(
        array(
            'table' => 'courses_modules',
            'alias' => 'CoursesModule',
            'type' => 'LEFT',
            'conditions' => array('CoursesModule.module_id = Module.id'),
        ),
        array(
            'table' => 'courses_users',
            'alias' => 'CoursesUser',
            'type' => 'LEFT',
            'conditions' => array('CoursesUser.course_id = CoursesModule.course_id'),
        ),
        array(
            'table' => 'modules_users',
            'alias' => 'ModulesUser',
            'type' => 'LEFT',
            'conditions' => array(
                'ModulesUser.module_id = Module.id',
                'ModulesUser.user_id' => $userId,
            ),
        ),
    ),
    'conditions' => array(
        'CoursesUser.user_id' => $userId,
        'ModulesUser.id' => null,
    ),
    'recursive' => -1,
));

哪个应该构建这样的查询:

SELECT Module.* FROM modules AS Module
LEFT JOIN courses_modules AS CoursesModule
    ON (CoursesModule.module_id = Module.id)
LEFT JOIN courses_users AS CoursesUser
    ON (CoursesUser.course_id = CoursesModule.course_id)
LEFT JOIN modules_users AS ModulesUser
    ON (ModulesUser.module_id = Module.id
        AND ModulesUser.user_id = $userId)
WHERE CoursesUser.user_id = $userId
AND ModulesUser.id IS NULL

前两个连接以及user_id条件将获得所有可用模块。而不是让用户,他们的课程,然后那个课程的模块,我们倒退(有点);我们获得模块并加入我们加入用户的课程。添加user_id条件后,将过滤关联,并且只能找到可以加入用户的模块。由于我们从模块开始不会有任何重复,我们不知道(或关心)使用哪个课程来建立链接。

使用这些可用模块,我们将加入用户已完成的模块,并将记录限制为无法连接的模块。

CakePHP文档提供了有关joining tables的更多信息。

编辑:

如果您想手动设置课程ID,可以执行以下操作:

$modules = $this->User->Module->find('all', array(
    'joins' => array(
        array(
            'table' => 'courses_modules',
            'alias' => 'CoursesModule',
            'type' => 'LEFT',
            'conditions' => array('CoursesModule.module_id = Module.id'),
        ),
        array(
            'table' => 'modules_users',
            'alias' => 'ModulesUser',
            'type' => 'LEFT',
            'conditions' => array(
                'ModulesUser.module_id = Module.id',
                'ModulesUser.user_id' => $userId,
            ),
        ),
    ),
    'conditions' => array(
        'CoursesModule.course_id' => $courseIds,
        'ModulesUser.id' => null,
    ),
    'recursive' => -1,
));

答案 1 :(得分:0)

你建立关系的方式有问题。您可以发布以显示您为建立关系所做的工作,以及提供错误的SQL查询是什么?我也对你的查询应该做什么感到困惑。

我已经可以做一些观察了:

  • 您的查询似乎没有定义课程,这就是您收到sql错误的原因。您的课程关系可能有问题。

  • 如果您尝试在用户和模块以及课程和模块之间建立HABTM关系,为什么要在Module和CoursesModules以及Module和UsersModules之间创建一个hasOne关系?

我想知道的简短版本,不应该是hasMany而不是hasOne吗?

长版本有时,当你有这样的连接表时,你需要出于某种原因与连接表本身建立关系,而不是仅仅使用HABTM(当你获得数据时,数据在连接表中不包括在内)。通常这是因为除了两个外键之外还有数据也必须存储在连接表中。但在这种情况下,你应该使用所谓的有很多关系。如果没有必要使用多次通过,您应该只使用HABTM与用户和课程,而不是直接与连接表创建关系。 http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany-through-the-join-model