【Yii系列】最佳实践之后台业务框架
缘起
上面的几章都讲概念了,没有怎么讲到实践的东西,可能会有些枯燥,这很正常的,概念还是需要慢慢啃的,尤其是官网其他的部分,需要狠狠的啃。
什么,你啃不动了?看看官网旁边的那个在线用户吧。
你不啃的时候可是有这么多人在啃知识,如果不想以后被这打击,赶紧学!!!一如当年大学的我,每天夜里都抱着一本《算法导论》在啃一样,自律相当重要。
这一章我就带大伙了解一下前两章的概念有啥用,应用到实际,这是临门一脚,但是,我总是觉得概念的重要性至少要占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', ], ];
这是全局配置,如果后面有新增模块,再往这里面加即可,新增了这几个文件,我们需要先完善这些代码。
首先,rules.php文件里面的配置项并无需要新增的代码。
modules里面定义了每个module的class,这边需要新增所有模块的基础模块类。
在modules文件夹里面新增定义好的几个module,并采用mvc结构初始化models,views,controllers,以及模块内的配置文件configs。
这边以Goods为例,我们建立商品模块。
第一步,在modules下面建立goods文件夹。并在goods目录下面创建对应的文件和mvc文件夹。
Goods.php文件为上面modules.php配置文件中goods模块的基类。用以引导goods模块,具体代码如下:
<?php namespace app\modules\goods; use Yii; class Goods extends \yii\base\Module { public $controllerNamespace = 'app\modules\goods\controllers'; public function init() { parent::init(); Yii::configure($this, require(__DIR__ . '/config.php')); } }
两个功能,指定controller namespace,加载配置文件。
config.php文件代码如下:
<?php return [ 'components' => [ // list of component configurations ], 'params' => [ // list of goods params ], ];
用以配置goods模块需要用到的配置项。
其他模块类似goods可以都创建一套相应的模板。
到此为止,相应的modules配置大功告成,以后撸代码就经常在这里面了。
回到配置文件,我们继续往下讲。加载了这么多文件,如何配置进去呢,不急,慢慢来。
首先,我们配置一下appid
这边我们是这么去配置的
'id' => Config('app.name'),
Config方法来源于Common.php文件定义的全局函数。
设置语言
'language' => 'zh-CN',
配置modules
'modules' => $modules,
配置别名
'aliases' => $aliases,
配置components,记得,这边已经到components里面啦。
配置urlManager,将上面的规则引到这边的配置项中
'urlManager' => [ 'showScriptName' => false, 'enablePrettyUrl' => true, 'rules' => $rules ],
配置log,按照我们上节讲到的区分环境配置。
'log' => [ 'traceLevel' => Config('app.log.traceLevel'), 'targets' => Config('app.log.targets'), ],
关于cache和db的配置,我们会到对应的章节中再做详解。
最后,需要将web/assets文件夹的权限设为可写,将runtime文件夹的权限设为可写。
至此,所有关于入口脚本的配置文件项和基础框架均已搭建完毕。
再次访问你的那个远程ip地址,出现下面这个页面表示成功。
创建接口
搭好了上面的框架,下面我们就先来创建一个接口试验一下吧。
首先,在User这个module下面的controller里面新建一个InfoController.php,用以获取用户的基本信息。
在InfoController.php里面,我们新建一个action,叫actionBaseInfo()。
具体代码如下:
<?php namespace app\modules\user\controllers; use Yii; use yii\web\Controller; class InfoController extends Controller { public function actionBaseInfo() { echo 'hello world!'; } }
Nginx虚拟主机配置
到这边,所有的准备工作都完成啦,现在,我们需要在Nginx里面配置一个可供远程访问的host。
首先,我们来到nginx的安装目录
#cd /usr/www/nginx/
进入配置文件夹
#cd conf
新建一个文件夹,用以存放vhost虚拟主机配置文件。
#mkdir vhosts
进入vhosts目录,新增sit.fengye.conf虚拟主机配置文件。
编辑一下内容到该文件里。
server { charset utf-8; client_max_body_size 128M; listen 80; server_name www.sit.fengye.com; index index.php; root /usr/www/app/yii-basic/web; location / { # 如果找不到真实存在的文件,把请求分发至 index.php try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { include fastcgi.conf; fastcgi_pass 127.0.0.1:9000; fastcgi_param ENV 'sit'; include fastcgi_params; #fastcgi_pass unix:/var/run/php5-fpm.sock; try_files $uri =404; } location ~ /\.(ht|svn|git) { deny all; } access_log /var/log/nginx/sit.fengye.access.log; error_log /var/log/nginx/sit.fengye.error.log; }
这些行代表什么意思,我会在Nginx的后续章节给大伙详解。
保存退出,需要让nginx在启动的时候加载虚拟主机配置,我们需要在刚才的conf目录下面的nginx.conf文件里面加一行。
include /usr/local/nginx/conf/vhosts/*.conf;
添加到http属性的最后一行即可。
保存退出,重启Nginx。
#service nginx restart
编辑你远程服务器的hosts和本地的hosts,让www.sit.fengye.com进入到hosts中,以便你顺畅的访问。
远程服务器编辑/etc/hosts,新增下一行
127.0.0.1 www.sit.fengye.com
本地编辑/etc/hosts【OS X】,新增下一行
服务器IP www.sit.fengye.com
至此,在浏览器中输入下面的链接,看到hello world,你就成功啦!!!
http://www.sit.fengye.com/user/info/base-info
结束语
好了,到此为止,证明之前的配置没有任何问题,路由规则也是能够搞通的,perfect!
关于数据库和缓存还有后续的框架内容,我还是会按照之前的方式,先讲概念,再讲实践。
关于本章的代码,以及后续的代码,枫爷都已发布到github上,供大伙下载,感兴趣的朋友别忘了Fork和Star一下我哈~感谢。
github地址:https://github.com/ifengye/yii-basic