我想要做的是一个简单的N-to-M连接,就像你从SQL中知道的那样。 表格的结构如下:
CREATE TABLE `groups` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`lastname` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`username` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`image` varchar(255) COLLATE utf8_unicode_ci NULL DEFAULT NULL,
`role_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
CREATE TABLE `groups_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`group_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
现在查询时,我希望包含用户与组相关的所有日期的行。 所以在我的理解中,我必须转到UserController并执行以下两个左连接(用户分组到group_users和groups_users)
$query2 = $this->Users->find();
$query2->join([
'a' => [
'table' => 'groups_users',
'type' => 'LEFT',
'conditions' => [
'a.user_id' => 'users.id',
'users.id' => 1
]
],
'b' => [
'table' => 'groups',
'type' => 'LEFT',
'conditions' => [
'b.id' => 'a.group_id'
]
]
]);
当然这是我的测试数据:
INSERT INTO `customytest`.`users` (`id`, `firstname`, `lastname`, `username`, `password`, `image`, `role_id`) VALUES (NULL, 'nm1', 'fn1', 'ln1', 'doesnt matter', NULL, '');
INSERT INTO `customytest`.`users` (`id`, `firstname`, `lastname`, `username`, `password`, `image`, `role_id`) VALUES (NULL, 'nm2', 'fn2', 'ln2', 'doesnt matter', NULL, '');
INSERT INTO `customytest`.`users` (`id`, `firstname`, `lastname`, `username`, `password`, `image`, `role_id`) VALUES (NULL, 'nm3', 'fn3', 'ln3', 'doesnt matter', NULL, '');
INSERT INTO `customytest`.`groups` (`id`, `name`) VALUES (NULL, 'group1');
INSERT INTO `customytest`.`groups_users` (`id`, `group_id`, `user_id`) VALUES (NULL, 1, 1);
INSERT INTO `customytest`.`groups_users` (`id`, `group_id`, `user_id`) VALUES (NULL, 1, 2);
INSERT INTO `customytest`.`groups_users` (`id`, `group_id`, `user_id`) VALUES (NULL, 1, 3);
因此,我只获取用户的数据,而不是相关的组数据:
{"id":1,"firstname":"nm1","lastname":"fn1","username":"ln1","password":"doesnt matter","image":null,"role_id":0},
{"id":2,"firstname":"nm2","lastname":"fn2","username":"ln2","password":"doesnt matter","image":null,"role_id":0},
{"id":3,"firstname":"nm3","lastname":"fn3","username":"ln3","password":"doesnt matter","image":null,"role_id":0}
答案 0 :(得分:1)
您正在使用高级ORM,因此我建议您使用适合您的匹配和拼接功能。具体而言,Query::contain()
,这将加入相关数据,并正确地保存结果,以便您最终得到合理的结果集和非重复数据。
这几乎是CakePHP 101,所以我建议在进入试用和错误模式之前将文档稍微研究一下。鉴于您已正确设置关联,您的查询将减少为
$this->Users
->find()
->contain('Groups')
->where([
'Users.id' => 1
]);
另见
当然也可以使用手动连接,但理想情况下,这对于非常复杂的查询和类似边缘情况应该是必需的。但是,为了完整起见,使用手动连接的查询的设置应该更像是这样:
$this->Users
->find()
->select($this->Users)
->select(['b.id', 'b.name'])
->join([
'a' => [
'table' => 'groups_users',
'type' => 'LEFT',
'conditions' => [
'a.user_id = users.id'
]
],
'b' => [
'table' => 'groups',
'type' => 'LEFT',
'conditions' => [
'b.id = a.group_id'
]
]
])
->where([
'Users.id' => 1
]);
使用手动连接时,您必须指定应选择的字段。由于您没有遵循命名约定,因此必须逐个指定它们。如果您遵循约定,并且存在适当的关联(这使得手动连接的使用变得多余),您可以使用select($this->Users->Groups)
- 连接看起来像:
'GroupsUsers' => [
'table' => 'groups_users',
'type' => 'LEFT',
'conditions' => [
'GroupsUsers.user_id = Users.id'
]
],
'Groups' => [
'table' => 'groups',
'type' => 'LEFT',
'conditions' => [
'Groups.id = GroupsUsers.group_id'
]
]
另请注意不使用键/值条件,这是必需的,否则您需要与字符串进行比较,例如a.user_id = 'users.id'
,这当然不起作用。此外,如果您计划将内容限制为特定用户,则不需要为连接表添加其他条件,因为用户ID比较已根据需要过滤了内容。
最后但并非最不重要的一点是,当用户在多个组中时,您可能会以这种方式收到重复数据,这是您不应该手动执行此类操作的另一个原因,Query::contain()
将处理事情的方式更优雅。
另见