【Yii系列】最佳实践之后台业务框架

时间:2022-04-07 23:29:46

缘起

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

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

【Yii系列】最佳实践之后台业务框架

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

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

环境

操作系统:OS X EI Capitan

PHP版本:PHP 5.6.30

Yii版本:Yii 2.0

编辑器:PHPStorm

整体框架

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

【Yii系列】最佳实践之后台业务框架

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

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

【Yii系列】最佳实践之后台业务框架

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

【Yii系列】最佳实践之后台业务框架

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

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

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

【Yii系列】最佳实践之后台业务框架

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这个文件夹中建立三个对应的目录,我们来先创建一下。

【Yii系列】最佳实践之后台业务框架

每个目录下面建立一个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的代码就如下所述:

【Yii系列】最佳实践之后台业务框架

<?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文件夹。

【Yii系列】最佳实践之后台业务框架

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地址,出现下面这个页面表示成功。

【Yii系列】最佳实践之后台业务框架

创建接口

搭好了上面的框架,下面我们就先来创建一个接口试验一下吧。

首先,在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

【Yii系列】最佳实践之后台业务框架

结束语

好了,到此为止,证明之前的配置没有任何问题,路由规则也是能够搞通的,perfect!

关于数据库和缓存还有后续的框架内容,我还是会按照之前的方式,先讲概念,再讲实践。

关于本章的代码,以及后续的代码,枫爷都已发布到github上,供大伙下载,感兴趣的朋友别忘了Fork和Star一下我哈~感谢。

github地址:https://github.com/ifengye/yii-basic