当前位置: 首页 > web开发 > PHP > 正文

Yii 2.0最佳实践之后台业务框架

时间:2017-04-26 博客园 枫爷

缘起

上面的几章都讲概念了,没有怎么讲到实践的东西,可能会有些枯燥,这很正常的,概念还是需要慢慢啃的,尤其是官网其他的部分,需要狠狠的啃。

什么,你啃不动了?看看官网旁边的那个在线用户吧。

你不啃的时候可是有这么多人在啃知识,如果不想以后被这打击,赶紧学!!!一如当年大学的我,每天夜里都抱着一本《算法导论》在啃一样,自律相当重要。

这一章我就带大伙了解一下前两章的概念有啥用,应用到实际,这是临门一脚,但是,我总是觉得概念的重要性至少要占70%,临门的一脚实践只占30%,望君能体会~

环境

操作系统:OS X EI Capitan

PHP版本:PHP 5.6.30

Yii版本:Yii 2.0

编辑器:PHPStorm

整体框架

首先,我们做这个框架的目的不是给我们自己看的,而是给广大以后会在这套框架中学习工作的人看的,所以,千万不能融入自己的思想,要尽可能的通俗易懂,符合一般的逻辑设计。

这张图是Yii提供给我们的源代码,首先,为了能够更能适合我们的业务框架,我决定来简单的修改一下这个文件结构。

首先,增加业务模块文件夹modules。用以区分每个不同的业务线。

增加全局基础类文件夹commons。用以定义application需要使用到的基础类。

在刚刚创建的Commons文件夹下面创建环境配置文件Config.php和全局方法文件Common.php

Config.php文件用以配置环境和获取相应环境的配置常量。

Common方法用以定义全局使用到的一些function。【注意,这边的Common只是用于保存全局的方法,不用做namespace】

 

Common里面比较重要的一个方法就是获取配置常量方法,后面在很多配置文件中会用到这个方法。

/**
 * 获取配置文件
 * @param $key string min;
 * @param string $env $string dev:开发环境
 * @return mixed
 */
function Config($key, $val = null)
{
    return \app\commons\Config::get($key, $val);
}

这边的Config就是我们刚才创建的Config.php文件,具体代码如下:

<?php
namespace app\commons;

/**
 * 主要实现不同文件配置查找扩展 file.param.param1
 *
 * 文件.数组变量.变量
 */
class Config
{
    const ENV_SIT = 'sit';

    const ENV_PRE = 'pre';

    const ENV_PRD  = 'prd';

    private static $_config = null;

    /**
     * 初始化配置,永远加载prd, 默认加载sit
     * @param type $configPath
     * @param type $env
     * @throws \Exception
     */
    public static function init($configPath = null, $env = self::ENV_SIT)
    {
        if (!is_dir($configPath)) {
            die('配置目录不存在');
        }

        $paths[] = $configPath . DIRECTORY_SEPARATOR . self::ENV_PRD;

        switch ($env) {
            case self::ENV_PRE:
                $paths[] = $devconfig = $configPath . DIRECTORY_SEPARATOR . self::ENV_PRE;
                break;
            case self::ENV_SIT:
                if (is_dir($configPath . DIRECTORY_SEPARATOR . self::ENV_SIT)) {
                    $paths[] = $configPath . DIRECTORY_SEPARATOR . self::ENV_SIT;
                }
                break;

            default:
                break;
        }

        static::$_config = \Noodlehaus\Config::load($paths);
    }

    public static function get($key, $default = null)
    {
        return static::$_config->get($key, $default);
    }

    public static function set($key, $value)
    {
        return static::$_config->set($key, $value);
    }
}

另外,我们需要在config文件夹中新增几个文件,用以区分线上环境【prd】,线上测试环境【pre】,本地开发环境【sit】的配置文件,具体的区分是在config这个文件夹中建立三个对应的目录,我们来先创建一下。

每个目录下面建立一个app.php的文件,用以存放app的相关配置常量。

那么,我们如何区分是哪个环境呢,以及如何对应到相应的环境配置常量中去呢。

这边,我给大伙带来了一个非常好用的配置第三方组件。Noodlehaus。

github地址:https://github.com/hassankhan/config

我们可以通过composer去下载和自动加载它。

$ composer require hassankhan/config

这里面有个问题,不知道是我学识不足,还是因为这个自动配置文件有问题,这个配置文件加载器始终不让我来按照文件名去区分配置变量,没办法,我暴力的修改了它的一行源代码。

打开Config的源代码,vendor/hassankhan/config/src/Config.php

修改构造函数里面的一行代码

// Try and load file
$this->data = array_replace_recursive($this->data, array($info['filename'] => (array) $parser->parse($path)));

将原本的(array) $parser->parse($path)修改为:array($info['filename'] => (array) $parser->parse($path))即可。

那么,为了保证这边的代码能够在第一时间被加载,以便于配置好环境常量,方便下面的操作,我们需要在入口脚本index.php处加上如下代码:

//我们每个环境的域名都会在Nginx虚拟配置里面设置,和环境有关
if (empty(getenv('ENV'))) {
    $hostInfo = $_SERVER['HTTP_HOST'];
    $environment = 'prd';
    if (strpos($hostInfo, 'sit') !== false) {
        $environment = 'sit';
    }

    if (strpos($hostInfo, 'pre') !== false) {
        $environment = 'pre';
    }
} else {
    $environment = in_array(getenv('ENV'), array('sit', 'pre', 'prd')) ?
        getenv('ENV') : 'sit';
}

\app\commons\Config::init(__DIR__, $environment);

这段代码的意思是如果没有设置环境,我们就根据_SERVER魔术变量中关于HTTP_HOST的值,去判断我们处理的应用主体处于哪个环境,这是一个灵活的配置,希望大家多思考思考这里面的思想。

但是,这样一来,我们的入口脚本就会变得很冗长,这是我们不愿意看到的,之前也和大家讲过,如果觉得在脚本中有过长的代码该如何,我们在config文件中新建一个bootstrap.php文件来存放上面的代码,包括include需要的两个文件,那么整体bootstrap.php的代码就如下所述:

<?php

ini_set('memory_limit', '128M');

//初始化全局函数
include dirname(__DIR__) . '/Commons/Common.php';
//初始化环境配置
include dirname(__DIR__) . '/Commons/Config.php';

... ... // 上面的代码

$getDebug = empty($_GET['debug']) ? '' : $_GET['debug'];

bootstrap.php撸完了,我们需要在入口脚本里面做一些小的改变,具体改变如下:

<?php

require(__DIR__ . '/../vendor/autoload.php');
require(__DIR__ . '/../config/bootstrap.php');

// comment out the following two lines when deployed to production
defined('YII_DEBUG') or define('YII_DEBUG', Config('app.debug'));
defined('YII_ENV') or define('YII_ENV', Config('app.env'));

require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');

$config = require(__DIR__ . '/../config/web.php');

(new yii\web\Application($config))->run();

上面这边已经使用了Config,这个方法是Common.php里面的一个方法,调用的就是Config.php里面的get方法,上面已经给大伙演示过啦,如果使用编辑器,应该会直接带出来,非常方便。

好的,到这边,我们对整个环境的区分配置就已经完成。现在就可以在app.php里面放置一些变量了~

示例为prd目录下app.php的配置代码。

<?php
/**
 * app.php 线上环境项目配置
 */

return [
    'name' => 'fengye-prd',
    'env' => 'prd',
    'debug' => false,
    'log' => [
        'traceLevel' => YII_DEBUG ? 3 : 0,
        'targets' => [
            [
                'class' => 'yii\log\FileTarget',
                'levels' => ['error', 'warning'],
            ],
            [
                'class' => 'yii\log\FileTarget',
                'categories' => ['fengye.info.*'],
                'levels' => ['info'],
                'logVars' => [],
            ]
        ],

    ]
];

其实吧,prd,pre,sit这三个目录下面还需要配置两个文件,一个是数据库配置文件,三个环境要予以区分;还有一个是缓存配置文件,redis,memcache的配置需要三个环境的区分。这会在后面讲完数据库和缓存再和大伙聊聊这边的配置的事。

对于MVC里的一些在commons里的基类,我们会放在模块中的MVC一节去讲解,看完了整体的一个需要改动的结构,我们再来看看配置文件的改动吧。

配置相关

在Yii系列基础框架中我们提到过一些基础的配置,至于如何实施,在那一章节中我们没有细讲,今天,在这一节中,我们来好好看下应用配置有哪些是必要的,哪些是不必要的。

上一节讲到,哪些配置是需要区分环境的,下面来讲的是通用配置,不需要区分环境的配置。

首先,我们打开配置文件,web.php。

<?php
$params = require(__DIR__ . '/params.php');

之前的章节中提到过,如果配置项中有太多的属性,需要列举到一个文件中,使整个代码结构更清晰。

这边,我们有几个配置项需要写到文件中。

在这段代码后新增

$rules = require(__DIR__ . '/rules.php');
$aliases = require(__DIR__ . '/aliases.php');
$modules = require(__DIR__.'/modules.php');

顺带在当前目录中【config】新增几个php文件:rules.php,aliases.php,modules.php

配置rules:

在Yii系列请求处理那一章中我们讲到一个路由规则,在具体的配置中,解析规则一节中我们提到一个rules配置项,使用正则去解析。

'<module:\w+>/<controller:\w+>/<action:\w+>' => '<module>/<controller>/<action>', //模块相关规则

前面使用户的URL,后面是解析到对应的路径,后面是路径哟,分别是modules、controller、action。

这边涉及到一个概念,接口版本的区分,比方说之前的老接口不能支持现在的新业务了,建议在接口action名称后面新增一个版本号,没有版本号的为默认版本,并在注释中使用注明,关于接口文档的修改,我觉得并没有那么麻烦,使用swagger自动生成在线文档即可,在修改接口版本的时候,去旧版本注释里面将旧街口标识为过期即可。

关于swagger的相关内容,我会在后面,Yii系列,第三方工具中详细讲解。

这条规则能满足大部分的情况,如果每个module下面有很多的子文件夹,就需要来重新定义一些规则啦。具体的看我到时候发布到github的源码吧。

aliases.php用于配置路径别名,这边我们先放一放,以后需要用到的时候再讲,这边暂时用不到。

modules.php文件用于配置各个业务模块,用以区分业务模块的代码区域。

<?php
/**
 * 配置业务模块
 */
return [
    // 用户模块
    'user' => [
        'class' => 'app\modules\user\User',
    ],
    // 商品模块
    'goods' => [
        'class' => 'app\modules\goods\Goods',
    ],
    // 订单模块
    'order' => [
        'class' => 'app\modules\order\Order',
    ],
    // 库存模块
    'stock' => [
        'class' => 'app\modules\stock\Stock',
    ],
    // 支付模块
    'pay' => [
        'class' => 'app\modules\pay\Pay',
    ],
    // 消息模块
    'message' => [
        'class' => 'app\modules\message\Message',
    ],
];