规划大型Yii2应用程序

时间:2017-08-24 06:10:09

标签: php design-patterns architecture yii2

我在考虑如何在Yii2中为客户端构建一个大型应用程序。我的经验来自几个较小的Yii2项目。

在第一步中必须做出哪些重大决策,以后不能轻易改变,以及典型的Yii2解决方案模式是什么?

以下是我正在考虑的一些功能:

用户管理

跳转开始是Yii2用户或Yii2-usario的扩展。提供用户管理,用户登录,密码重置功能等。

多租户

要在一个数据库中管理多个客户端,建议将客户端ID添加到每个表,并使用Yii2行为将此表字段添加到每个数据库查询。

可选/复杂功能

Yii2为代码分离提供了“模块”。 Yii2模块可以包含组件,模型,视图,控制器......,非常适合在以后阶段提供独立功能。或者将功能与核心应用程序分开。

是否有类似的Yii2模式从项目开始就知道,以避免项目期间的重大重构?

2 个答案:

答案 0 :(得分:2)

Yii2中提供的一个重要模式是 RBAC (角色授权访问)..

如果应用程序很大,请记住您可能需要 Internazionalization 来表示日期,货币和合成以及多语言

审核以查看访问权限的人员(此处也有良好的扩展名)

答案 1 :(得分:1)

在yii2中,您可以从一个代码库运行多个应用程序。 yii2的高级模板为您提供了很好的起点,但您可能拥有多个“前端”应用程序。这将帮助您共享或拆分配置(包括数据库)为您的前端。因此,您可以在应用程序中重用常用模块,并可以自由地执行完全不同的操作。

也许这超出了范围,但是为了实现多租户,我确实只能通过行为限制对数据行的访问。 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;
    }

}

?>