使用CakePHP 2.2,我正在构建一个应用程序,其中每个客户端都拥有自己的“领域”数据,并且其他任何数据都不可见。例如,客户有他的一组用户,课程,承包商和工作。组在客户端之间共享,但它们无法对组执行操作。所有客户端都可以使用组将其分配给用户。因此,管理员(使用ACL)只能管理来自同一客户端ID的数据。
我的所有对象(当然除了组)都有client_id键。
现在,我知道一种方法可以完成这项工作,并且确实让它运转良好,但它似乎有点脏,我想知道是否有更好的方法。在项目的早期和CakePHP的新手,我渴望得到正确的。
这就是我现在正在做的事情:
1-用户登录。他的client_id根据用户表中的数据写入会话。
$user = $this->User->read(null, $this->Auth->user('id'));
$this->Session->write('User.client_id', $user['User']['client_id']);
2-在AppController中,我有一个受保护的函数,它将该会话ID与给定参数进行比较。
protected function clientCheck($client_id) {
if ($this->Session->read('User.client_id') == $client_id) {
return true;
} else {
$this->Session->setFlash(__('Invalid object or view.'));
$this->redirect(array('controller' => 'user', 'action' => 'home'));
}
}
3-我的不同索引操作(每个索引,每个相关控制器),我使用分页条件检查client_id。
public function index() {
$this->User->recursive = 0;
$this->paginate = array(
'conditions' => array('User.client_id' => $this->Session->read('User.client_id'))
);
$this->set('users', $this->paginate());
}
4-在其他操作中,我在检查HTTP请求类型之前检查client_id。
$user = $this->User->read(null, $id);
$this->clientCheck($user['User']['client_id']);
$this->set('user', $user);
答案 0 :(得分:1)
这个概念很好 - 它并不“脏”,而且与我处理这类情况的方式完全一样。
你刚刚获得了几行冗余代码。第一:
$this->Auth->user('id')
该方法实际上可以为登录用户获取任何字段,因此您可以执行以下操作:
$this->Auth->user('client_id')
所以你的两行:
$user = $this->User->read(null, $this->Auth->user('id'));
$this->Session->write('User.client_id', $user['User']['client_id']);
不需要。您无需重新读取用户,也无需向会话写任何内容 - 只需在您需要时直接从Auth获取client_id。
事实上,如果您阅读http://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#accessing-the-logged-in-user,它甚至表示您可以从控制器的上下文之外获取它,使用静态方法,如:
AuthComponent::user('client_id')
虽然看起来你不需要那样。
答案 1 :(得分:1)
您还可以通过在模型中的beforeFind函数中放置一些东西,将client_id条件应用于Model的所有查找。
例如,在您的用户模型中,您可以执行以下操作:
function beforeFind( $queryData ) {
// Automatically filter all finds by client_id of logged in user
$queryData['conditions'][$this->alias . '.client_id'] = AuthComponent::user('client_id');
return $queryData;
}
不确定AuthComponent :: user('client_id')是否在模型中有效,但您明白了。这将自动将此条件应用于模型中的每个查找。
您还可以使用模型中的beforeSave在新记录中自动为您设置client_id。
答案 2 :(得分:0)
我的答案可能是数据库引擎,因为我使用PostgreSQL。在我的项目中,我在mysql术语中为每个客户端使用了不同的模式,这些模式对于每个客户端都是独立的数据库。
在公共模式(公共数据库)中,我存储了所有客户端(在您的情况下没有client_id的对象)之间需要共享的所有数据,例如,变量常量,配置文件设置等。
在公司特定的模型中我定义
public $useDbConfig = 'company_data';
在Controller/AppController.php
beforeFilter()
方法中,我使用此代码根据登录用户设置架构。
if ($this->Session->check('User.Company.id')) {
App::uses('ConnectionManager', 'Model');
$dataSource = ConnectionManager::getDataSource('company_data');
$dataSource->config['schema'] =
'company_'.$this->Session->read('User.Company.id');
}
如您所见,我根据二手公司动态更新dataSource。这确实排除了company_id在任何查询中的任何参与,因为只有公司相关数据存储在该模式(数据库)中。这也增加了扩展项目的能力。
这种方法的缺点是,它会在结构更改中同步所有数据库结构时产生痛苦,但可以使用导出数据,删除所有数据库,使用新布局重新创建数据库并再次导入数据来完成。只需要确保导出包含列名称的完整插入数据。