我在考虑如何在Yii2中为客户端构建一个大型应用程序。我的经验来自几个较小的Yii2项目。
在第一步中必须做出哪些重大决策,以后不能轻易改变,以及典型的Yii2解决方案模式是什么?
以下是我正在考虑的一些功能:
用户管理
跳转开始是Yii2用户或Yii2-usario的扩展。提供用户管理,用户登录,密码重置功能等。
多租户
要在一个数据库中管理多个客户端,建议将客户端ID添加到每个表,并使用Yii2行为将此表字段添加到每个数据库查询。
可选/复杂功能
Yii2为代码分离提供了“模块”。 Yii2模块可以包含组件,模型,视图,控制器......,非常适合在以后阶段提供独立功能。或者将功能与核心应用程序分开。
是否有类似的Yii2模式从项目开始就知道,以避免项目期间的重大重构?
答案 0 :(得分:2)
Yii2中提供的一个重要模式是 RBAC (角色授权访问)..
如果应用程序很大,请记住您可能需要 Internazionalization 来表示日期,货币和合成以及多语言
审核以查看访问权限的人员(此处也有良好的扩展名)
答案 1 :(得分:1)
也许这超出了范围,但是为了实现多租户,我确实只能通过行为限制对数据行的访问。 where子句自动应用于所有选择,因此客户端只能返回他拥有的那些行。在您的代码中,您现在可以选择并加入,而无需考虑所有权。
<强> ActiveRecord.php 强>
<?php
namespace common\models;
use Yii;
use yii\helpers\Url;
class ActiveRecord extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
'group' => [
'class' => \x1\data\behaviors\GroupBehavior::className(),
'map' => ['gid' => 'group_id'],
'className' => \common\models\Group::className(),
],
];
}
public static function checkAccess() {
if (!Yii::$app instanceof \yii\console\Application) {
$user = Yii::$app->get('user', false);
$identity = ($user) ? $user->getIdentity() : null;
if (empty($identity)) {
if (!empty($user->loginUrl))
return Yii::$app->getResponse()->redirect($user->loginUrl);
else
throw new \yii\web\UnauthorizedHttpException;
}
}
}
//
// select only rows within the user's group,
// except for console app
//
public static function find() {
self::checkAccess();
return (new ActiveQuery(get_called_class()))->current();
}
}
?>
<强> GroupBehaviour.php 强>
<?php
namespace x1\data\behaviors;
use Yii;
use yii\base\Event;
use yii\db\BaseActiveRecord;
/*
class myModel extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
'group' => [
'class' => \x1\data\behaviors\GroupBehavior::className(),
'map' => ['gid' => 'group_id'],
'className' => \common\models\Group::className(),
],
];
}
}
*/
class GroupBehavior extends \yii\behaviors\AttributeBehavior
{
public $map = ['gid' => 'group_id'];
public $className = null;
public $value;
public function getGroup() {
return $this->owner->hasOne($this->className, $this->map);
}
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if ($this->className == null) {
throw new \yii\base\InvalidConfigException("'className' must be set");
}
if (!is_array($this->map)) {
throw new \yii\base\InvalidConfigException("'map' must be an array; e.g.: ['gid' => 'group_id']");
} else {
if (!count($this->map) > 0) {
throw new \yii\base\InvalidConfigException("'map' must contain the mapping group => local; e.g.: ['gid' => 'group_id']");
}
}
if (!Yii::$app instanceof \yii\console\Application) {
if (empty($this->attributes)) {
$this->attributes = [
BaseActiveRecord::EVENT_BEFORE_INSERT => array_values($this->map)[0],
];
}
}
}
/**
* Evaluates the value of the user.
* The return result of this method will be assigned to the current attribute(s).
* @param Event $event
* @return mixed the value of the user.
*/
protected function getValue($event)
{
if ($this->value === null) {
$user = Yii::$app->get('user', false);
$group = array_keys($this->map)[0];
return ($user && !$user->isGuest) ? $user->identity->group->$group : null;
} else {
return call_user_func($this->value, $event);
}
}
}
<强> ActiveQuery.php 强>
<?php
namespace common\models;
use Yii;
class ActiveQuery extends \yii\db\ActiveQuery
{
private $_alias = null;
private function getAlias() {
if ($this->_alias === null) {
if (empty($this->from)) {
$modelClass = $this->modelClass;
$tableName = $modelClass::tableName();
} else {
foreach ($this->from as $alias => $tableName) {
if (is_string($alias)) {
$this->_alias = $alias;
return $this->_alias;
} else {
break;
}
}
}
if (preg_match('/^(.*?)\s+({{\w+}}|\w+)$/', $tableName, $matches)) {
$this->_alias = $matches[2];
} else {
$this->_alias = $tableName;
}
}
return $this->_alias;
}
public function current()
{
$alias = $this->getAlias();
if (!Yii::$app instanceof \yii\console\Application)
$this->andWhere(['IN', sprintf('COALESCE(%s.group_id,0)', $alias), [0, Yii::$app->user->identity->group_id]]);
return $this;
}
public function rawSql() {
return $this->prepare(Yii::$app->db->queryBuilder)->createCommand()->rawSql;
}
}
?>