Lumen项目整合

一、安装

通过 Lumen 安装器

首先,使用 Composer 下载 Lumen 安装包:

1
composer global require "laravel/lumen-installer"

请确保你已将 ~/.composer/vendor/bin 路径添加到环境变量 PATH 中,只有这样系统才能找到 lumen 的可执行文件。如何让安装Composer?

一旦安装完成,使用 lumen new 将会在您指定的目录中创建一个新的Lumen 项目。例如: lumen new blog 命令将会创建一个名字叫 blog 的目录 ,此目录里面存放着新安装的 Lumen 和代码依赖。这个方法的安装速度比通过 Composer 安装要快很多:

1
lumen new blog

此方法的缺点也很明显,无法安装指定的lumen版本.需要安装指定版本的Lumen可参考下面的方法.

通过 Composer Create-Project 命令安装

你也可以在你电脑的终端输入 create-project 命令来安装 Lumen :

1
composer create-project  laravel/lumen blog  --prefer-dist "5.2.*"

二、项目目录

在lumen 基础上添加了 Models 目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/
|-- app 项目应用目录
| |-- Console 自定义的 Artisan 命令
| |-- |-- Kernel.php 注册自定义的 Artisan 命令以及定义调度任务
| |-- Events 存放事件类
| |-- Extends 存放自定义扩展,例如Excel导出等
| |-- Exceptions 异常处理
| | |-- Handler.php 处理应用抛出的异常
| |-- helpers.php 自定义函数方法
| |-- Http 主程序
| | |-- Middleware 中间件
| | |-- Controllers 控制器
| | | |-- Admin 后台目录
| | | |-- V1 V1版本
| | | |-- Logic 业务逻辑
| | | |-- Api 前台目录
| | | |-- V1 V1版本
| | | |-- Logic 业务逻辑
| | | |-- Pub 公共目录
| | | |-- V1 V1版本
| | |-- Logic 业务逻辑
| | |-- Controller.php 版本父级控制层
| | |-- Logic.php 版本父级逻辑层
| |-- Jobs 队列任务
| |-- Listeners 事件监听器
| |-- Providers 服务提供
| |-- Models 数据模型目录
|-- bootstrap 自动加载配置
| |-- app.php 启动、自动加载配置
|-- config 所有应用配置文件
| |-- auth.php auth授权配置
| |-- cors.php cors跨域配置
| |-- database.php database配置
|-- database 数据迁移、填充文件
|-- public Web站点根目录
|-- resources 视图、原生资源文件
| |-- lang 语言目录
|-- routes 路由配置文件
| |-- admin 后台管理路由配置
| |-- admin.php
| |-- api 前台路由配置
| |-- api.php
| |-- pub 公共路由配置
| |-- pub.php
|-- storage 缓存、框架生成文件
| |-- app 存放应用生成的文件
| |-- framework 框架生成的文件或缓存
| |-- logs 日志文件
|-- tests 自动化测试
|-- vendor Composer依赖文件
|-- .env 配置文件
|-- .env.example 配置文件备份
|-- composer.json
\

三、 数据模型

3.1 命名规范

数据模型相关的命名规范:

  • 数据模型类名 必须 为「单数」, 如:App\Models\Photo
  • 类文件名 必须 为「单数」,如:app/Models/Photo.php
  • 数据库表名字 必须 为「复数」,多个单词情况下使用「Snake Case」 如:photos, my_photos
  • 数据库表迁移名字 必须 为「复数」,如:2014_08_08_234417_create_photos_table.php
  • 数据填充文件名 必须 为「复数」,如:PhotosTableSeeder.php
  • 数据库字段名 必须 为「Snake Case」,如:view_count, is_vip
  • 数据库表主键 必须 为「id」
  • 数据库表外键 必须 为「resource_id」,如:user_id, post_id
  • 数据模型变量 必须 为「resource_id」,如:$user_id, $post_id

3.1 Mysql模型

在app目录下创建数据模型 Models

在数据模型开头修改命名空间

1
2
3
4
5
6
7
8
# 在app根目录的数据模型 命名空间为:
namespace App;

#修改为命名空间为
namespace App\Models;

#使用 Models下的User模型
use App\Models\User;

其他目录的添加同上,只是在命名空间上做修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
namespace App\Models;

use App\Extend\TraitDbEloquent;
use Illuminate\Database\Eloquent\Model;

class BaseModel extends Model
{
use TraitDbEloquent;

// default db
protected $connection = 'mysql';

public function __construct()
{
parent::__construct();
}
}

3.2 Mongo模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace App\Mongo;

use App\Extend\TraitDbEloquent;
use Jenssegers\Mongodb\Eloquent\Model as Eloquent;

class BaseModel extends Eloquent
{
use TraitDbEloquent;

// primary key
protected $primaryKey = "_id";

// primary key type
protected $keyType = "string";

// default db
protected $connection = 'mongodb';

public function __construct()
{
parent::__construct();
}
}

其中 TraitDbEloquent 封装了数据库CURD操作。

四、统一输出

在helpers.php中统一处理输出模块,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* 统一输出
* @param int $code
* @param string $msg
* @param array $data
* @return array
*/
function format_return($code, $msg = null, $data = null)
{
$res = [
'code' => $code,
'msg' => is_null($msg) ? trans("reponse.$code") : $msg
];
if (!is_null($data)) {
$res['data'] = $data;
}
return $res;
}

其中trans定义了状态信息,放在 ./resources/lang/文件,方便支持多语言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// zh-CN/reponse.php 中文简体
<?php

return [
2000 => '请求成功',
4000 => '请求失败',
];

//en/reponse.php 英文
<?php

return [
2000 => 'ok',
4000 => 'fail',
];

五、 JWT配置

5.1 安装

通过composer安装jwt-auth

1
composer require tymon/jwt-auth "1.*"

注意:jwt-auth 0.5.* 版本未对lumen做封装

5.2 修改自动加载配置 文件 bootstrap/app.php

  • 去掉 $app->withFacades() $app->withEloquent() 的注释
  • 添加 jwt 接口
1
2
3
4
5
6
7
8

//使用 Facades 静态类
$app->withFacades(true,[
'Tymon\JWTAuth\Facades\JWTAuth' => 'JWTAuth',
'Tymon\JWTAuth\Facades\JWTFactory' => 'JWTFactory'
]);

$app->withEloquent(); //使用 Eloquent ORM
  • 去掉 auth 中间件 注释
1
2
3
4
//auth 中间件
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);
  • 去掉appServiceProvider的注释,并且在 AppServiceProvider 中注册 LumenServiceProvider
1
2
3
$app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\AuthServiceProvider::class);
$app->register(\Tymon\JWTAuth\Providers\LumenServiceProvider::class);

5.3 jwt配置

  • 获取 auth 配置文件
    在 lumen 根目录下 创建 config 文件夹 (laravel 框架 自动加载 config 文件夹的内容),并将 vendor/laravel/lumen-framework/config 中的 auth.php 文件复制到刚刚创建的config文件夹中。修改 auth.php 文件,将 api 认证指定为 jwt,并绑定users 数据模型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//原
'guards' => [
'api' => ['driver' => 'api'],
],

//修改为:
'guards' => [
'api' => ['driver' => 'jwt'],
'provider' => 'users'
],


//指定数据模型
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => \App\Models\User::class,
],
],
  • JWT 协议需要用到 secret,所以需要生成一个 secret,在根目录下 执行命令
1
2
3
php artisan jwt:secret
//执行成功返回如下
jwt-auth secret [Y] set successfully.

执行成功后,会把生成的secret写入 .env 文件中
并配置jwt token 的三个时间

1
2
3
4
5
6
7
JWT_SECRET=xxxxxxxxxx
//有效时间 单位:分钟
JWT_TTL = 60
//刷新时间 单位:分钟 默认 14天
JWT_REFRESH_TTL = 20160
//宽限时间 单位:秒
JWT_BLACKLIST_GRACE_PERIOD = 60

作者更喜欢使用这个JWT扩展类 https://github.com/firebase/php-jwt

六、异步事件处理

在很多场景中我们需要某些特定函数后台运行,例如 访问量+1 、发送短信通知、日志存储等,同步的做法是在业务逻辑执行完成以后,在执行文件末尾添加 相关的访问量统计等方法。 这对程序的效率是由很大的影响的,采用异步处理可以有效处理此类业务场景。
lumen提供了 events 事件处理机制。以日志监控为例,我们需要对用户请求、程序响应记录在日志服务器上面,这一块我们只需要在Middleware中拦截入口流量和出口流量,记录即可。代码如下:

  • 注册EventServiceProvider
1
2
// Event
$app->register(App\Providers\EventServiceProvider::class);
  • 中间件流量拦截

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    <?php
    namespace App\Http\Middleware;

    use App\Events\MonitorEvent;
    use Closure;


    class Monitor
    {
    /**
    * Handle an incoming request.
    *
    * @param \Illuminate\Http\Request $request
    * @param \Closure $next
    * @param string|null $guard
    *
    * @return mixed
    */
    public function handle($request, Closure $next, $guard = null)
    {
    // 开始计时
    $start = microtime(true);
    // 执行业务
    $response = $next($request);
    // 结束计时
    $end = microtime(true);

    // 流量监控
    if (ENV('OPEN_TRAFFIC_MONITOR', true)) {
    $params = [
    'ip' => $request->getClientIp(),
    'host' => $request->getHost(),
    'route' => $request->path(),
    'method' => $request->method(),
    'header' => $request->header(),
    'params' => limit_var_size($request->all(), 1024),
    'response' => limit_var_size($response->getOriginalContent(), 1024),
    'require_time' => date('Y-m-d H:i:s', $start),
    'response_time' => date('Y-m-d H:i:s', $end),
    'ttl' => $end - $start,
    'status' => $response->getStatusCode(),
    ];
    // 触发事件
    event(new MonitorEvent($params));
    }

    return $response;
    }
    }
  • 事件接收器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php

namespace App\Events;

use Illuminate\Queue\SerializesModels;

abstract class Event
{
use SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public $data;

public function __construct($data = [])
{
$this->data = $data;
}

public function getData()
{
return $this->data;
}
}
  • 事件监听处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

namespace App\Listeners;

use App\Events\Event;
use App\Http\Controllers\Pub\V1\Logic\MonitorLogic;

class MonitorListener extends Listener
{

/**
* Handle the event.
*
* @param $event
* @return mixed
*/
public function handle(Event $event)
{
return (new MonitorLogic())->run($event->getData());
}
}
  • 注册事件处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php  
namespace App\Providers;

use Laravel\Lumen\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'App\Events\MonitorEvent' => [
'App\Listeners\MonitorListener',
],
];
}

最重要的一步, .env配置中 QUEUE_DRIVER=sync 改为 QUEUE_DRIVER=redis,即把同步队列设置为异步队列。

配置完成,启动队列: php artisan queue:listen

关注作者公众号,获取更多资源!
赏作者一杯咖啡~