1

所以我有三个表,用户、组和 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 之前,我下面的代码运行良好,现在,我得到了我需要的所有数据,但我无法保存。

所以,我的组模型看起来像这样:

<?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

SQL 错误信息

所以用户组模型没有被注册,当我删除文件时没有任何变化,它试图使用我以前用来删除旧连接数据的方法的名称作为 SQL 命令。我已经尝试了所有可能的建议来命名我在 Stack 上找到的数据的结构和结构,但失败了,我得到的最远的是当我只有一个要保存的连接项时,总是数组中的最后一个......

任何人都可以帮助解决这个问题吗?

干杯,

O。

4

1 回答 1

3

保持传统

这里的主要问题似乎是由非常规引起的

表名

文档描述了以下内容:

这个新连接表的名称需要包含所涉及的两个模型的名称,按字母顺序排列,并用下划线 (_) 分隔

因此,默认情况下,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'
    )
);

对不存在的模型函数的调用变成 sql 查询

错误:SQLSTATE[42000]:语法错误或访问冲突:1064 您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的“deleteAllWithGroup”附近使用正确的语法 SQL 查询:deleteAllWithGroup

这一切都表明,对一个没有实现被调用函数的类进行了查询。这可以通过检查对象的类来验证:

debug($this->UsersGroup);
// Most likely "AppModel"

请注意,连接模型本身没有定义任何关联,因此这样做:

$this->UsersGroup->unbind(...);

不会产生任何影响 - 关联是在问题中的模型 User 和 Group 上定义的,即使UsersGroup要加载该类 - 它没有定义任何关联,更不用说与其他事物的 habtm 关系(这需要总共5张桌子!)

最后,可能也是最重要的:这个函数不是必需的

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向返回的结果添加键。这导致更少和更简单的代码。

于 2013-11-10T17:30:49.333 回答