我想通过模型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:
我正在尝试执行的模型查询发生在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()
将Courses
与Modules
联系起来。这很奇怪,因为关联路径是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;
答案 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