所以我有三个表,即用户,组和users_groups,它们是一个连接表。
--
-- Table structure for table `groups`
--
CREATE TABLE `groups` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
`description` text NOT NULL,
`all_versions_available` tinyint(1) unsigned NOT NULL DEFAULT '1',
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `name` (`name`,`created`,`modified`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=12 ;
-- --------------------------------------------------------
--
-- Table structure for table `users`
--
CREATE TABLE `users` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(40) NOT NULL,
`email` varchar(80) NOT NULL,
`password` varchar(50) NOT NULL,
`role` varchar(20) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`fullname` varchar(80) NOT NULL,
`password_token` varchar(40) NOT NULL,
PRIMARY KEY (`id`),
KEY `nickname` (`username`,`email`,`password`),
KEY `role` (`role`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=6 ;
-- --------------------------------------------------------
--
-- Table structure for table `users_groups`
--
CREATE TABLE `users_groups` (
`user_id` int(11) unsigned NOT NULL,
`group_id` int(11) unsigned NOT NULL,
KEY `user_id` (`user_id`,`group_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
在我的组和用户模型中实现HABTM之前,我在下面的代码工作正常,现在,我得到了我需要的所有数据,但我无法保存。
所以,我的Group Model看起来像这样:
<?php
class Group extends AppModel {
public $hasAndBelongsToMany = array(
'Application' => array(
'className' => 'Application',
'joinTable' => 'applications_groups',
'foreignKey' => 'group_id',
'associationForeignKey' => 'application_id',
'unique' => 'keepExisting',
),
'User' => array(
'className' => 'User',
'joinTable' => 'users_groups',
'foreignKey' => 'group_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
)
);
public $validate = array(
'name' => array(
'required' => array(
'rule' => array('notEmpty'),
'message' => 'Group name is required'
)
)
);
public function saveGroup($id, $name, $description) {
$id = (int)$id;
if ($id) {
$this->id = $id;
}
else {
$this->create();
}
$this->set('name', $name);
$this->set('description', $description);
$this->save();
return $this;
}
public function getAll() {
$options = array('order' => array('Group.name' => 'ASC'));
$data = $this->find('all', $options);
return $data;
}
public function getOne($id) {
$id = (int)$id;
return $this->find('first', array('conditions' => array('Group.id' => $id)));
}
}
我的用户模型如下所示:
<?php
class User extends AppModel {
public $hasAndBelongsToMany = array(
'Group' => array(
'className' => 'Group',
'joinTable' => 'users_groups',
'foreignKey' => 'group_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
)
);
public function getOne($id) {
$this->id = $id;
$data = $this->read(null, $id);
unset($data['User']['password']);
unset($data['User']['password_token']);
if (isset($data['User'])) $data['User']['gravatar_url'] = 'http://www.gravatar.com/avatar/'.md5($data['User']['email']).'.jpg';
return $data;
}
private function addGravatars($data) {
foreach ($data as $key=>$user) {
$data[$key]['User']['gravatar_url'] = 'http://www.gravatar.com/avatar/'.md5($user['User']['email']).'.jpg';
}
return $data;
}
public function getAll() {
$data = $this->find('all', array('order' => array('User.fullname' => 'ASC')));
$data = $this->addGravatars($data);
return $data;
}
public function countAll() {
return $this->find('count');
}
}
我一直在使用模型作为连接表:
<?php
class UsersGroup extends AppModel {
public function deleteAllWithGroup($groupId) {
$id = (int)$groupId;
return $this->deleteAll(array('UsersGroup.group_id' => $id), false);
}
public function saveUsersForGroup($users, $groupId=0) {
$this->deleteAllWithGroup($groupId);
$data = array();
foreach ($users as $id=>$user) {
$data[] = array('user_id'=>(int)$id, 'group_id'=>$groupId);
}
$this->saveMany($data);
}
}
这是我的群组控制器:
<?php
class GroupsController extends AppController {
var $uses = array('Group', 'User', 'UsersGroup');
public function index() {
$this->set('groups', $this->Group->getAllWithInfo());
}
public function edit($id=0) {
$this->set('group', $this->Group->getOne($id));
$this->set('usersList', $this->User->getAllWithGroupInfo($id));
if ($this->request->is('post')) {
$group = $this->Group->saveGroup($this->request->data['id'], $this->request->data['name'], $this->request->data['description']);
// Saving users
if (!isset($this->request->data['user']) || empty($this->request->data['user'])) {
$this->UsersGroup->deleteAllWithGroup($group->id);
}
else $this->UsersGroup->saveUsersForGroup($this->request->data['user'], $group->id);
}
}
public function view($id) {
App::uses('Platforms', 'Lib/Platform');
$this->setPageIcon('group');
$this->set('group', $this->Group->getOne($id));
}
public function delete($id) {
$this->Group->delete((int)$id);
return $this->redirect(array('action' => 'index'));
}
}
有几个问题,如果我删除HABTM配置,上面的系统是有效的,第二,我没有,由于一些非常具体的原因,没有使用表单助手来生成表单,不幸的是,代码的复杂性(这只是一点点)我不能这样,我必须自己手动命名一切(这是我看到失败的最大可能性),最后当我解雇这段代码时,我得到:
Database Error
Error: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'deleteAllWithGroup' at line 1
SQL Query: deleteAllWithGroup
Notice: If you want to customize this error message, create app/View/Errors/pdo_error.ctp
所以UsersGroup模型没有被注册,当我甚至删除文件时没有任何变化,它试图使用我以前用来删除旧连接数据的方法的名称作为SQL命令。我已经尝试了所有可能的建议,我在堆栈上找到的数据的命名和结构但是失败了,我得到的最远的是当我只有一个连接项目要保存时,总是数组中的最后一个...
任何人都可以帮忙解决这个问题吗?
干杯,
0
答案 0 :(得分:3)
这里的主要问题似乎是非常规的
造成的The docs描述以下内容:
这个新的连接表的名称需要包括所涉及的两个模型的名称,按字母顺序排列,并用下划线分隔(_)
因此默认情况下,CakePHP会期望将此类关系的连接表称为groups_users
。
鉴于上述情况,该关系的连接模型将为GroupsUser
。定义hasAndBelongsToMany关系如下:
public $hasAndBelongsToMany = array(
'Group' => array(
'className' => 'Group',
'joinTable' => 'users_groups',
'foreignKey' => 'group_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
)
);
意味着CakePHP将仍然尝试并使用名为GroupsUser
的模型为其提供表名users_groups
。要强制使用不同的连接模型,需要定义要使用的模型 - with
:
public $hasAndBelongsToMany = array(
'Group' => array(
'className' => 'Group',
'joinTable' => 'users_groups',
'foreignKey' => 'group_id',
'associationForeignKey' => 'user_id',
'unique' => 'keepExisting',
'with' => 'UsersGroup'
)
);
虽然重命名连接表和连接模型会更好,但是配置可以简化为以下内容,因为其他一切都是默认值:
public $hasAndBelongsToMany = array(
'Group' => array(
'unique' => 'keepExisting'
)
);
错误:SQLSTATE [42000]:语法错误或访问冲突:1064 SQL语法中有错误;检查与MySQL服务器版本对应的手册,以便在第1行的“deleteAllWithGroup”附近使用正确的语法 SQL查询:deleteAllWithGroup
所有这些都证明,是在对不实现被调用函数的类上进行了查询。这可以通过检查对象的类来验证:
debug($this->UsersGroup);
// Most likely "AppModel"
请注意,连接模型本身 not 没有定义任何关联,因此这样做:
$this->UsersGroup->unbind(...);
将无效 - 关联是在问题中的模型User和Group上定义的,即使要加载类UsersGroup
- 它没有定义任何关联,更不用说与habtm的关系了别的东西(总共需要5张桌子!)
最后,也许最重要的是:这个函数isn't necessary:
HABTM数据被视为一个完整的集合,每次添加新的数据关联时,数据库中的完整关联行集将被删除并再次创建
修复代码不会导致问题,因此该方法被称为,除之外,无论保存是否成功,连接表记录都会被删除;而CakePHP的逻辑只会在成功时删除连接表记录。
虽然在模型上创建方法来封装逻辑没有任何问题 - 如果使用现有模型api很容易表达该逻辑,那么所有这些都会使代码更难以读取/调试。像这样的代码:
public function getOne($id) {
$this->id = $id;
$data = $this->read(null, $id);
unset($data['User']['password']);
unset($data['User']['password_token']);
if (isset($data['User'])) $data['User']['gravatar_url'] = 'http://www.gravatar.com/avatar/'.md5($data['User']['email']).'.jpg';
return $data;
}
可以轻松地使用find('first')
调用替换并向用户模型添加afterFind
过滤器,以便为返回的结果添加gravatar_url
个键。这导致代码更少,更简单。