草庐IT

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

coder 2023-10-25 原文

简而言之这个问题
我想通过模型B的控制器中的find()操作从模型A中检索数据,而不依赖于广泛的递归。

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

我知道这样做需要bindModel(),但如果没有多次递归,我似乎无法访问关联的模型字段(即不仅是habtm表的字段,而是实际关联的模型)。
我突然意识到,我也可能从根本上误解了模型关系应该如何相互作用、如何设计、如何检索等等。简言之,我认识到,我可能不会成功的原因是,这可能不是我应该做的事情,亨利。如果是这样的话,我也很乐意学习如何更好地做到这一点,因为我经常处理非常复杂的模型关系(我主要为学术界和在线课程材料/远程研究做web开发)。
具体实例/实际情况
数据库中有这些表,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未知表“course”
但我也不能用bindModel()连接CoursesModules。这让我觉得很奇怪,因为关联路径是Users->Courses->Modules。我可以将递归提高一个级别,但它会导致各种各样的地狱,需要大量的unbind(),并且还提取了非常荒谬的数据量。
第二个奇怪的地方是,如果我从fields列表中删除Course.*,上面的查询将执行,但不会像我所期望的那样工作;我认为这是正确的,它要求coursesmodules中列出的所有模块都不在usersmodules中。这样的数据确实存在于我的记录中,但并不是由此检索的。
我意识到我可以从course_ids中获取CoursesModules,然后再做另一个查找以获取课程模型数据,但这是a)不太像蛋糕,b)很痛苦,因为我非常希望能够访问呈现视图文件中的$modules['Module']['Course']
有人能给我指个正确的方向吗?或者,哈哈,上帝保佑,帮我建立这个mysql查询(我对mysql连接很在行)?非常感谢,谢谢!
更新
@kai:为了建立关系,我建立了自己的表并烘焙它们。值得注意的是,也许我对mysql有一个相当基本的了解,并且通常都是通过phpmyadmin来完成的。至于生成最初的cake文件,我使用cake bake all并在运行时修改了一些内容。表的sql和相应模型中的$hasAndBelongsToMany数组将在末尾发布。
至于我为什么选择hasOne。我还假设hasMany;使用此关系从我正在绑定的表中一致地生成了“column not found”错误(与哪一列无关)。同时,明显错误的选择在一定程度上起了作用。
最后,我有一个潜在的怀疑,这可能是我想要的生意,但我真的不明白。尽可能简单地说,这是这些模型的上下文以及我试图执行的查询类型:
我正在为一所大学的教员建立一个项目,基本上让教授们有一些在线课程。但课程作业(即模块)可能在不同的班级(即课程)之间共享,学生可能在任何班级或所有班级。另一个限制是,学生可以选择在给定课程中学习哪些模块,而教授可以提供五个模块,其中任何三个模块都必须完成。所以,当一个学生登录时,我需要能够检索他们还没有完成的模块,在他们所学课程的上下文中。
我有很多类似的查询,或多或少都是这种性质的。现在,我可以通过各种各样的hasOne,执行一个更简单的containable behavior,通过一些loadModel()逻辑对结果进行排序,冲洗重复,来实现所有这些(由于这是在最后期限内完成的)。除了恼人之外,我还担心它无法扩展,因为我的部分处理不当,最后…我讨厌做错事情,哈哈。我知道cakephp可以处理我想问的关于我的数据的问题,我只是不知道如何问他们(和/或设置数据以便可以问这些问题)。
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;

模型关联
注:这是不全面的hasone,hasmany,belongsto等已被省略,以节省空间;如果需要我可以张贴整个模型。
// from User Model
public $hasAndBelongsToMany = array(
        'Course' => array(
            'className' => 'Course',
            'joinTable' => 'users_courses',
            'foreignKey' => 'user_id',
            'associationForeignKey' => 'course_id',
            'unique' => 'keepExisting',
        ),
        'Module' => array(
            'className' => 'Module',
            'joinTable' => 'users_modules',
            'foreignKey' => 'user_id',
            'associationForeignKey' => 'module_id',
            'unique' => 'keepExisting',
        )
    );

// from Course Model
    public $hasAndBelongsToMany = array(
        'Module' => array(
            'className' => 'Module',
            'joinTable' => 'courses_modules',
            'foreignKey' => 'course_id',
            'associationForeignKey' => 'module_id',
            'unique' => 'keepExisting',
            'conditions' => '',
            'fields' => '',
            'order' => '',
            'limit' => '',
            'offset' => '',
            'finderQuery' => '',
        ),
        'User' => array(
            'className' => 'User',
            'joinTable' => 'users_courses',
            'foreignKey' => 'course_id',
            'associationForeignKey' => 'user_id',
            'unique' => 'keepExisting',
            'conditions' => '',
            'fields' => '',
            'order' => '',
            'limit' => '',
            'offset' => '',
            'finderQuery' => '',
        )
    );

// from Module Model
    public $hasAndBelongsToMany = array(
        'Excerpt' => array(
            'className' => 'Excerpt',
            'joinTable' => 'excerpts_modules',
            'foreignKey' => 'module_id',
            'associationForeignKey' => 'excerpt_id',
            'unique' => 'keepExisting',
            'conditions' => '',
            'fields' => '',
            'order' => '',
            'limit' => '',
            'offset' => '',
            'finderQuery' => '',
        ),
        'Course' => array(
            'className' => 'Course',
            'joinTable' => 'courses_modules',
            'foreignKey' => 'module_id',
            'associationForeignKey' => 'course_id',
            'unique' => 'keepExisting',
            'conditions' => '',
            'fields' => '',
            'order' => '',
            'limit' => '',
            'offset' => '',
            'finderQuery' => '',
        )
    );

最佳答案

据我所知,这就是问题所在;用户在其课程中有可用模块的列表。我们要查找用户尚未完成的可用模块。换言之,我们必须找到不在用户模块中的用户课程模块。
我能想到的最好的办法是:

$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

前两个连接以及用户id条件将获得所有可用的模块。与其让用户,他们的课程,然后那个课程的模块,我们倒过来工作(有点);我们得到模块并加入我们加入用户的课程。添加用户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,
));

关于mysql - 通过bindModel()w/o递归访问HABTM关系的关联模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20341136/

有关mysql - 通过bindModel()w/o递归访问HABTM关系的关联模型的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  4. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  5. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  6. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  7. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  8. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  9. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  10. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

随机推荐