lumen

HTTP路由

  • 基本路由
  • 路由参数
    1. 必填参数
    2. 可选参数
    3. 正则表达式约束
  • 命名路由
  • 路由组
    1. 中间件
    2. 命令空间
    3. 路由前缀
基本路由

你可以在 route/web.php 文件中定义应用程序的全部路由。最基本的Lumen路由仅接受URL和一个Closure:

$router->get('foo', function(){
    return 'Hello, World';
});

$router->post('foo', function(){
    //
});
可供使用的路由方法

我们可以注册路由来响应任何方法的HTTP请求:

$router->get($uri, $callback);
$router->post($uri, $callback);
$router->put($uri, $callback);
$router->patch($uri, $callback);
$router->delete($uri, $callback);
$router->options($uri, $callback);
路由参数
必填参数

当然,有时需要在路由中捕获一些URL片段。例如,从URL中捕获用户的ID,可以通过通过定义路由参数来执行此操作:

$router->get('user/{id}', function ($id) {
    return "User:" . $id;
});

也可以根据需要在路由中定义多个参数:

$router->get('posts/{postId}/comments/{commentId}', function ($postId, $commentId) {
    //
});

路由的参数都会被放在[大括号]内。当运行路由时,参数会传递到Closure里面。

注意:路由参数不能包含-字符。请用下划线_代替。

可选参数

你可以通过将部分路由URI包含在[...]中来定义可选的路由参数。那么像/foo[bar]将会匹配到/foo和/foobar。可选参数仅支持放在URI的末尾。换句话说,你不能在路由定义的中间位置放置可选参数:

$router->get('user'[/{name}]', function ($name = null) {
    return $name;
});
正则表达式约束

你可以通过在路由定义中使用正则表达式来约束路由参数的格式:

$router->get('user2/{name:[a-zA-Z]}', function () {
    //
});
命名路由

命名路由可以方便的为特定路由生成URL或者进行重定向。你可以使用as数组键指定名称到路由上:

$router->get('profile', ['as' => 'profile', function() {
    //
}]);

你还可以指定控制器行为的路由名称:

$router->get('profile', [
    'as' => 'profile',
    'uses' => 'UserController@showProfile',
]);
生成指定路由的URL

为路由制定了名称后,就可以使用全局辅导函数route来生成链接或者重定向到该路由:

//Generating URLs...
$url = route('profile');

//Generating Redirects...
return redirect()->route('profile');

如果是有定义参数的命名路由,可以把参数作为route函数的第二个参数传入,指定的参数将会自动插入到URL中对应的位置:

$router->get('user/{id}/profile', ['as' => 'profile', function ($id) {
    return $id;
}]);

$url = route('profile', ['id' => 1]);
路由组

路由群组允许你共用路由属性,例如:中间件、命名空间,你可以利用路由组统一为多个路由设置共同属性,而不需要在每个路由都设置一次。共用属性被指定为数组格式,当做$router->group方法的第一个参数。

为了了解更多路由群组的相关内容,我们可以通过几个常用样例来熟悉这些特性。

中间件

要给路由组中所有的路由分配中间件,你可以在group属性数组中使用middleware字段。中间件会依照它们在数组中列出的顺序来运行:

$router->group(['middleware' => 'auth'], function () use ($router) {
    $router->get('/', function () {
        //使用Auth中间件
    });

    $router->get('user/profile', function () {
        //使用Auth 中间件
    });
});
命名空间

另一个常见的例子是,指定相同的PHP命名空间给控制器群组。可以使用namespace参数来指定群组内所有控制器的命名空间:

$router->group(['namespace' => 'Admin'], function () use ($router) {
    //使用"App\Http\Controllers\Admin"命名空间
    $router->group(['namespace' => 'User', function () use ($router) {
        //使用"App\Http\Controllers\Admin\User" 命名空间...
    }]);
});

路由前缀

通过路由群组数组属性中的prefix,在路由群组内为每个路由指定的URI加上前缀。例如,你可能想要在路由群组中将所有的路由URI加上前缀admin:

$router->group(['prefix,' => 'admin'], function () use ($roter) {
    $router->get('users', function () {
        //匹配The "/admin/users"URL
    });
});

你也可以使用prefix参数去指定路由群组中共用的参数:
$router->group(['prefix' => 'accounts/{accountId}'], function () use ($router) {
$router->get('detail', function ($accountId) {
//匹配The "/accounts/{accountId}/detail" URL
});
});

HTTP中间件
  • 简介
  • 定义中间件
  • 注册中间件

    • 全局中间件
    • 为路由指定中间件
  • 中间件参数
  • Terminal中间件

简介

HTTP中间件提供了一个方便的机制来过滤进入应用程序的HTTP请求。例如,lumen内置了一个中间件来验证用户的身份认证。如果用户未通过省份证,中间件将会把用户导向登录页面,反之,当用户通过了身份证,中间件将会通过此请求并接着往下执行。

当然,除了身份证之外,中间件也可以被用来运行各式各样的任何,如:CORS中间件负责替换所有即将离开程序的响应加入适当的标头;而日志中间件则可以记录所有出入应用从程序的请求。

所有的中间件都放在app/Http/Middleware目录内。

定义中间件

你可以通过赋值lumen内置的示例文件ExampleMiddleware来创建一个中间件。在这个中间件中,我们只允许参数age大于200的请求才能访问该路由。否则,我们将此用户重定向到首页"home"这个URI上。

正如你所见,假如给定的age参数小于或者等于200,这个中间件将返回一个HTTP重定向到客户端;否则,请求将进一步传递到应用中。要让请求继续传递到应用程序中(即允许“通过”中间件验证的),只需要使用$request作为参数去调用回调函数$next。

最好将中间件想象为一系列HTTP请求必须经过才能进入你的应用的层。每一个都会检查请求
(是否符合某些条件),(如果不符合)甚至可以(在请求访问你的应用之前)完全拒绝掉。

前置/后置中间件

中间件是在请求之前或者之后运行取决于中间件本身。例如,接下来的这个中间件将在应用处理请求before执行其任务:

<?php
namespace App\Http\Middleware;
use Closure;
class BeforeMiddleware
{
public function handle($request, Closure $next)
{
//执行操作
return $next($request);
}
}

而接下来的这个中间件将在应用处理请求之后执行其任务:

<?php
namespace App\Http\Middelware;
use Closure;

class AfterMiddleware
{
    public function handle($request, Closure $next)
    {
        return $next($request);
    }
}

注册中间件

全局中间件

若是希望中间件在应用处理每个HTTP请求期间运行,只需要在bootstrap/app.php文件中的
$app->middleware()方法中列出这个中间件:

$app->middleware([
   App\Http\Middleware\OldMiddleware::class
]);

为路由分配中间件

如果你想将中间件分配给特定的路由,首先需要在bootstrap/app.php文件中调用$app->routeMiddleware()方法时为中间件分配一个简短的键:

$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,]);

一旦在HTTP内核中定义好了中间件,就可以在路由选项内使用middleware键:

$router->get('admin/profile', ['middleware' => 'auth', function () {
    //
}]);

可以使用数组为路由指定多个中间件:

$router->get('/', ['middleware' => ['first', 'second'], function () {
    //
}]);

中间件参数

中间件也可以接受自定义传参,例如,要在运行特定操作前检查已验证用户是否具备该操作的“角色”,可以创建RoleMiddleware来接受角色名称作为额外的传参。

附加的中间件参数将会在$next参数之后被传入中间件:

<?php
namespace App\Http\Middleware;

use Closure;

class RoleMiddleware
{
    /**
    *运行请求过滤
    *
    *@param \Illuminate\Http\Request $request
    *@param \Closure $next
    *@param string $role

    *@return mixed
    */
    public function handle($request, Closure $next, $role)
    {
        if (!$request->user()->hasRole($role)) {
            //重定向...
        }
        
        return $next($request);
    }
}

在路由中可以使用冒号:来区隔中间件名称与指派参数,多个参数可以使用逗号作为分割:

$router->put("post/{id}", ['middleware' => 'role:editor', function($id) {
    //
}]);

Terminable 中间件

有时中间件可能需要在HTTP响应发送到浏览器哦之后处理一些工作。例如,“session”中间件会在响应发送到浏览器之后将会话数据写入存储器中。想要做到这一点,你需要定义一个名为“terminable”的中间并添加一个terminal方法:

<?php
    
namespace Illuminate\Session\Middleware;

use Closure;

class StartSession
{
    public function handle($request, Closure $next)
    {
        return $next($request);
    }

    public function terminate($request, $response)
    {
        //存储session数据
    }
}

terminate方法应该同时接受和响应。一旦定义了这个中间件,你应该将它添加路由列表或者bootstrap/app.php文件的全局中间件中。

在你的中间件上条用terminite调用时,lumen会从服务器中解析出一个新的中间件实例。如果你希望在handle及terminate方法被调用时使用一致的中间件实例,只需要在容器中使用容器的singleton方法注册中间件。

HTTP控制器

简介

为了替代把所有的请求处理逻辑都定义在routes.php路由文件中,你或许想要使用控制来组织这些行为。控制器可以把相关的请求处理逻辑整合为一个控制器类。控制器类文件被存储在app/HTTP/Controllers目录下。

基础控制器

这里有一个基础控制器的示例。所有lumen的控制器都应该继承lumen安装时内置的默认的控制器基类:

<?php

namespace App\Http\Controllers;

use App\User;

class UserController extends Controller
{
/**
 * 获取指定 ID 的用户
 *
 * @param  int  $id
 * @return Response
 */
public function show($id)
{
return User::findOrFail($id);
}
}

我们可以像下面这样路由指向控制器的方法:

$router->get("user/{id}", "UserController@show");

现在,当请求匹配到这个特定的URL时,UserController类中的show方法就会执行。当然,路由的参数也同样传递给了这个方法。

控制器和命名空间

有一点非常重要,那就是我们要注意在定义控制器路由时,不需要指定完整的控制器命名空间。我们只需定义“根”命名空间App\Http\Controllers之后的类名部分。默认情况下,bootstrap/app.php文件在加载routes.php时已经把所有路由都配置到了根控制器命名空间。

如果你选择在App\Htpp\Controllers目录内层使用PHP命名空间嵌套或者组织控制器,只要使用相对于App\Http\Controllers根命名空间的特定名称即可。因此,如果你的控制器类全名是App\Http\Controllers\Photos\AdminController,那么你应该注册一个路由,如下所示:

$router->get('foo', 'Photos\AdminController@method');

命名控制器路由

像闭包那样,你可以给控制器路由指定一个名称:

$router->get('foo', ['uses' => 'FooController@method', 'as' => 'name']);

你也可以使用route辅助函数,来生成指向控制器路由的URL:

$url = route('name');

控制器中间件

中间件可通过如下方式分配到路由中:

$router->get('profile', [
    'middleware' => 'auth',
    'uses' => 'UserController@showProfile'
]);

然而,更方便的方式是在控制器的构造方法里面使用middleware方法指定中间件。你甚至可以限制中间件只应用于该控制器类的某些方法:

class UserController extends Controller
{
/**
实例化一个新的UserController实例。
@return void
*/
public function __construct()
{
$this->middleware('auth');

    $this->middleware('log', [
        'only' => ['fooAction', 'barAction']
    ]);

    $this->middleware('subscribed', ['except' => [
        'fooAction',
        'barAction',
    ]]);
}

}

依赖注入与控制器

构造器注入

lumen使用[服务容器]来解析所有的控制器的依赖注入。因此,你可以在控制器的构造函数中使用类型提示需要的任何依赖。这些依赖会自动的解析并注入到控制器实例中:

namespace App\Http\Controllers;

use App\Repositories\UserRepository;

class UserController extends Controller
{
    /**
    *新建一个控制器实例
    *
    *@param UserRepository $users
    *@return void
    */
    public function __contruct(UserRepository $users)
    {
        $this->users = $users;
    }
}

方法注入

除了构造器注入以外,你也可以在你的控制器方法中使用类型提示依赖。例如,在某个方法中添加Illuminate\Http\Request实例的类型提示:

<?php
    namespace App\Http\Controllers;
    use Illuminate\Http\Request;
    
    class UserController extends Controller
    {
        /**
        *保存一个新用户
        *@param Request $request
        
        *@return Response
        */
        public function store()
        {
            $name = $request->input('name');
        }
    }

如果你想在控制器里获取路由参数,只需要在路由之后列出参数即可。例如,你的路由这样来定义:
$router->put('user/{id}', 'UserController@update');

你可以像下面的例子一样定义你的控制器,用类型提示注入Illuminate\Http\Request类和你的路由参数id:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
    *更新指定用户
    *
    *@param Request $request
    *@param string $id
    *@return Response
    */
    public function update(Request $request, $id)
    {
        //
    }
}

HTTP请求

获取请求实例

要通过依赖注入的方式获取当前HTTP请求的实例,你应该在控制器构造函数或者方法中使用
Illuminate\Http\Request类,当前请求实例将自动被服务容器注入:

<?php

namespace App\Http\Controller;

use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
    * 保存新用户
    * @param Request $request
    * @return Response
    */
    public function store(Request $request)
    {
        $name = $request->input('name');
        //
    }
}

如果你的控制器方法也期望从路由参数中获取数据,只需要将路由参数放在其他依赖后面,比如你的路由是这样定义的:

$router->put('user/{id}', 'UserController@update');

像下面这样定义你的控制器方法,就可以使用Illuminate\Http\Request类型提示,同时获取到路由参数id:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
    *更新指定用户
    *
    *@param Request $request
    *@param string $id
    *
    *@return Response
    */
    public function update(Request $request, $id)
    {
        //
    }
}

基本请求信息

Illuminate\Http\Request 实例继承了Symfony\Component\HttpFoundation\Request类,并提供了多种检查HTTP请求的方法,下面是该类的几个使用方法:

获取请求的URI

path方法会返回请求的URI,如果请求的目标是http://domain.com/foo/bar,path方法将会返回foo/bar:

$uri = $request->path();

is方法会返回请求的URI是否与指定规则匹配,你可以使用*符号作为通配符:

if($request->is('admin/*')) {
    //
}

如果要获取完整的URL而不是URI,可以使用url或者fullURL方法:

//不包含请求参数
$url = $request->url();

//包含请求参数
$url = $request->fullUrl();

获取请求的方法

method方法将会请求的HTTP动作,你可以使用isMethod方法校验HTTP动作是否与指定字符串匹配:

$method = $request->method();

if ($request->isMethod('post')) {
    //
}

PSR-7请求

PSR-7标准规定了HTTP消息接口包含了请求及响应,如果你想获得PSR-7的请求实例,就需要先
安装几个库,laravel使用Symfony的HTTP消息桥组件,将原laravel的氢气及响应转换至PSR-7所支持的实现:

composer require symfony/psr-http-message-bridge

composer require zendframework/zend-diactors

安装完这些库后,你就可以在路由或者控制器中,简单的对请求类型使用类型提示来获取PSR-7请求:

use Psr\Http\Message\ServerRequestInterface;

$router->get('/', function (ServerReuestInterface $request) {
    //
});

如果你从路由或者控制器反悔了一个PSR-7的响应实例,那么它会被框架自动转换为laravel的响应实例并显示。

获取输入数据

获取指定输入值

你可以通过Illuminate\Http\Request实例,使用几个简单的方法来获取所有的用户输入数据,而不需要担心请求的HTTP动作,因为它们的获取方式是相同的:

$name = $request->input('name');

你可以在input方法的第二个参数中传入一个默认值,当请求参数不存在时,就会返回默认值:

$name = $request->input('name', 'Sally');

当数据是以数组形式输入时,你可以使用“点”符号来获取数组:

$name = $request->input('products.0.name');

$name = $request->input('products.*.name');

确认输入值是否存在

你可以通过has方法判断输入值是否存在,输入值存在时has方法将会返回true:

if($request->has('name')) {
    //
}

当给定一个数组时,has方法将确认是否所有指定值都存在:

if ($request->has(['name', 'email'])) {
    //
}

如果你想确定请求中是否存在值并且不为空,可以使用filled方法:

if($request->filled('name')) {
    //
}

获取所有输入数据

你可以使用all方法以数组形式获取所有的输入数据:

$input = $request->all();

获取部分输入数据

如果你想获取数据的子集,你可以是only和except方法,这两个方法都接受单个数组或者动态列表作为参数:

$input = $request->only(['username', 'password']);

$input = $request->only('username', 'password');

$input = $request->except(['credit_card']);

$input = $request->except('credit_card');

文件上传

获取上传文件

你可以使用Illuminate\Http\Request实例中的发file方法获取上传的文件,file方法返回的对象是Symfony\Component\HttpFoundation\File\UploadFile类的实例,这个类继承了PHP的SplFileInfo类,并且提供了多种与文件交互的方法:

$file = $request->file('photo');

你可以使用hasFile方法确认上传的文件是否存在:

if ($request->hasFile('photo')) {
    //
}

验证上传是否成功

除了检查文件是否存在之外,你还可以通过isValid方法验证上传是否存在问题:

if ($request->file('photo')->isValid()) {
    //
}

移动上传文件

要将上传的文件移动到新的位置,你应该使用move方法,这个方法会将文件从临时位置(由PHP配置决定)移动到你指定永久存储位置:

$request->file('photo')->move($destinationPath);

$request->file('photo')->move($destinationPath, $fileName);

其他上传文件方法

UploadFile实例还有很多其他可用的方法,可以到该类的API文档了解这些方法的详细信息。

HTTP响应

基本响应

当然,所有的路由及控制器必须返回某个类型的响应,并发送回用户的浏览器。laravel提供了集中不同的方法来返回响应。最基本的响应就是从路由或者控制器简单的返回一个字符串:

$router->get('/', function () {
    return 'Hello World';
});

指定的字符串会被框架自动转为HTTP响应。

响应对象

但是,对于大多数路由和控制器行为操作,你将返回完整的Illuminate\Http\Response实例。返回完整的Response实例允许你自定义响应的HTTP状态码和标题。一个Response实例继承自Symfony\Componet\HttpFoundation\Response类,并且提供了多种构建HTTP响应的方法:

use Illuminate\Http\Response;

$router->get('home', function () {
    return (new Response($content, $status))
        ->header('Content-Type', $value);
});

为了方便起见,你可以使用response辅助函数:

$router->get('home', function () {
    return response($content, $status)
        ->header('Content-Type', $value);
});

注意:有关Response方法的完整列表可以参考API文档一级Symfony api文档。

附加标头至响应

大部分的响应方法是可以链式调用的,折让你可以顺畅的创建响应。举例来说,你可以在响应发送给用户之前,使用header方法增加一系列的标头至响应:

return response($content)
    ->header('Content-Type', $type)
    ->header('X-header-One', 'header Value')
    ->header('X-Header-Two', 'header Value');

或者你可以使用withHeaders方法来设置数组标头:
return response($content)
->withHeaders([
'Content-Type' => $type,
'X-header-One' => 'Header Value',
'X-header-Two' => 'Header Value',
]);

其他响应类型

使用辅助函数response可以轻松的生成其他类型的响应实例、当你调用辅助函数response并且不带任何参数时,将会返回Laravel\Lumen\Http\ResponseFactory contract的实现。此Contract提供了一些有用的方法来生成响应。

JSON响应

json方法会自动将标头的Content-Type设置为application/json。并且通过PHP的接送_encode函数将制定的数组转换为json:

return response()->json(['name' => 'Abigail', 'state' => 'CA']);

你可以选择提供一个状态码和一个额外的标题数组:

return response()->json(['error' => 'Unauthorized'], 401, ['X-Header-One' => 'Header Value']);

如果你想创建一个JSONP响应,则可以使用json方法并加上setCallback方法:

return response()
    ->json(['name' => 'Abigail', 'state' => 'CA'])
    ->setCallback($request->input('callback'));

文件下载

download方法可以用于生成强制让用户的浏览器下载指定路劲文件的响应。download方法接受文件名称作为方法的第二个参数,此名称为用户下载文件时看见的文件名称。最后,你可以传递一个标头的数组作为第三个参数传入该方法:

return reponse()->download($pathToFile);

return response()->download($pathToFile, $name, $headers);

注意:管理文件下载的扩展包Symfony HTTPFoundation,要求下载文件必须是ASCII文件名。

重定向

重定向响应是类illuminate\Http\RedirectResponse的实例,并且包含用户要重定向至另一个URL所需的正确标头。有几种方法可以生成RedirectResponse的实例。最简单的方法就是通过redirect辅助函数:

$router->get('dashboard', function () {
    return redirect('home/dashboard');
});

重定向至命名路由

当你调用redirect辅助函数并且不带任何参数时,将会返回laravel\Lumen\Http\Redirecotr的实例,你可以对该Redirector的实例调用任何方法。举个例子,要生成一个RedirectResponse到一个命名路由,你可以使用route方法:

return redirect()->route('login');

如果你的路由有参数,则可以将参数放进route方法的第二个参数,如下:

//For a route with follwing URI:profile/{id}
return redirect()->route('profile', ['id' => 1]);

如果你要重定向至路由并且路由的参数为Eloquent模型的[ID],则可以直接将模型传入,ID将会自动被提取:

return redirect()->route('profile', [$user]);

用户认证

简介

lumen虽然与laravel使用了相同的底层类库实现,但是因lumen面向的是无状态API的开发,不支持session,所以默认的配置不同。lumen必须使用无状态的机制来实现认证,如api令牌(Token)。

开始
认证服务提供者

注意:在使用lumen的认证功能前,请取消bootstrap/app.php文件中的AuthServiceProvider调用代码的注释。

AuthServiceProvider存放在app/Providers文件夹中,此文件中只有一个Auth::viaRequest调用。viaRequest会在系统需要认证的时候被调用,此方法接受一个Closure(匿名函数)参数。在此closure(匿名函数)内,你可以任意的解析App\User并返回,或者在解析失败时返回null:

$this->app['auth']->viaRequest('api', function ($request) {
    //返回User或者null...
});

同样,你可以使用你期望的方式取得用户认证,比如在请求头或者查询字符串中使用api令牌、请求中的bearer令牌,或者使用应用程序需要的任何其他方法。

如果你的项目没有使用Eloquent,你需要返回一个Illuminate\Auth\GenericUser类的实例。这个类接受一个属性数组作为构造函数的唯一参数:

use Illuminate\Auth\GenericUser;

return new GenericUser(['id' => 1, 'name' => 'Taylor']);

缓存

简介

laravel为各种缓存系统提供了统一的api。缓存配置位于.env文件中。在该文件中你可以指定应用默认使用哪个缓存驱动。laravel支持当前流行的后端缓存,例如memcached和Redis。

不同于laravel

lumen缓存驱动与laravel缓存驱动使用了完全相同的代码。除配置之外,在lumen中使用缓存和在laravel中使用缓存没有区别;因此,请参阅laravel文档来获取使用示例。

注意:在使用cache facade之前,请确保在bootstrap/app.php文件中没有注释掉$app->withFacaes()方法的调用。

Redis支持

在使用lumen的Redis缓存之前,你需要通过composer安装illuminate/redis(5.5.*)包。然后,你需要在bootstrap/app.php文件中注册illuminate\Redis\RedisServiceProvider。

如果你没有在bootstrap/app.php文件中调用$app->withEloquent(),那么你应该在bootstrap/app.php文件中调用$app->configure('database');以确保正确加载Redis数据库配置。

缓存

简介

laravel为各种缓存系统提供了统一的api。缓存配置位于.env文件中。在该文件中你可以指定应用默认使用哪个缓存驱动。laravel支持当前流行的后端缓存,例如Memcached和Redis。

不同于laravel

lumen缓存驱动与laravel缓存驱动使用了完全相同的代码。除配置之外,在lumen中使用缓存和在laravel中使用缓存没有区别;因此,请参阅laravel文档来获取使用示例。

注意:在使用Cache Facade之前,请确保在bootstrap/app.php文件中没有注释掉$app->withFacdes()方法的调用。

Redis支持

在使用lumen的Redis缓存之前,你需要通过composer安装illuminate/redis(5.5.*)包。然后,你需要在bootstrap/app.php文件中注册illuminate\Redis\RedisServiceProvider。

如果你没有在bootstrap/app.php文件中调用$app->withEloquent()。那么你应该在bootstrap/app.php文件中调用$app->configure();以确保正确加载Redis数据库配置。

数据库

配置

lumen让连接数据库和执行查询变得非常简单。目前lumen支持四种数据库系统:MySQL,Postgres,SQLite和SqlServer。

你可以在.env配置文件中使用DB_*选项配置数据库设置,例如数据库驱动、host、用户名和密码。

基本用法

注意:如果你想使用DB facade,你应该去掉在bootstrap/app.php文件中$app->withFacades()的调用的注释。

例如,在不启用facades时,你可以通过app辅助函数连接数据库:

$result = app('db')->select("SELECT * FROM users");

或者,在启用facades后,你可以通过DB facade来连接数据库:

$result = DB::select("SELECT * FROM users");

基本查询

要了解如何通过数据库组件执行基本的原始SQL查询,请参考laravel文档

查询构造器

lumen同样支持Eloquent ORM的流式查询构造器。要了解这个特性的更多信息,请参阅laravel文档。

Eloquent ORM

如果你喜欢使用Eloquent ORM,你应该去掉bootstrap/app.php文件中对$app->withQloquent()调用的注释。
当然,你可以在lumen中非常容易的使用完整的Eloquent ORM。要了解如何使用Eloquent,请参考laravel文档。

迁移

关于如何创建数据库表和执行迁移的更多文档,请参考laravel文档中的迁移。

加密与解密

配置

在使用lumen的加密之前,你应该先把.env文件中APP_KEY选项设置为32位随机字符串。如果没有适当地设置这个值,所有被lumen加密的值都将是不安全的。

基本用法

加密一个值

你可以使用crypt门面来加密一个值。所有的加密值都使用OpenSSL和AES-256-CBC来进行加密。此外,所有加密过的值都会使用消息认证码(MAC)来进行签名,以检测加密字符串是否被篡改过:

例如,我们可以使用encrypt方法加密机密信息,并把它保存在Eloquent模型:

<?php
namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;

class UserController extends Controller
{
    /**
    * 保存用户的机密信息。
    * @param Request $request
    * @param int $id
    * @return Response
    */
    public function storeSecret(Request $request, $id)
    {
        $user = User::findOrFail($id);
        
        $user->fill([
            'secret' => Crpty::encrypt($request->secret)
        ])->save();
    }
}

加密一个值

当然,你可以使用crypt门面的decrypt方法来解密值。如果该值不能够被正确的解密。例如当MAC(消息认证码)无效时,就会抛出异常Illuminate \Contracts\Encryption\DecryptException:

use Illuminate\Contracts\Encryption\DecryptException;

try{
    $desrypted = Crypt::decrypt($encryptedValue);
}catch (DecryptException $e){
    //
}

错误与日志

简介

当你开始一个新的Lumen项目时,lumen就已经帮你配置好错误和异常处理的操作。另外,lumen也集成了Monolog日志函数库,Monolog支持和提供多种强大的日志处理功能。

有关错误的更多信息,请参阅完整的laravel错误文档。
有关日志的更多信息,请参阅完整的laravel的日志文档。

错误处理

介绍

当你启动一个新的laravel项目时,错误及异常处理是已为你配置好了的。
App\Exceptions\Handler类负责记录应用程序触发的所有异常并呈现给用户。在本文档中,我们将深入探讨这个类。

配置

你的config/app.php配置文件中的debug选项决定了对于一个错误实际上讲显示多少信息给用户。默认情况下,该选项的设置将遵循存储在.env文件中的APP_DEBUG环境变量的值。

对于本地开发,你应该将APP_DEBUG环境变量的值设置为true。在生产环境中,该值赢始终未false。如果在生产将该值设置为true,则可能会将敏感配置暴露给应用程序的最终用户。

异常处理器

report方法

所有异常都是由App\Exceptions\Handler类处理。这个类包含两个方法:report和rendor。我们将详细剖析这些方法。report方法用户记录异常或将它们发送给如Bugsnag或者Sentry等外部服务。默认情况下,report方法将异常传递给记录异常的基类。不过,你可以任何自己喜欢的方式来记录异常。

例如,如果你需要以不同方式报告不同类型的异常,则可以使用PHP的instanceof比较运算符:

/**
*报告或者记录异常
*
*此处是发送异常给sentry、Bugsnag等外部服务的好位置。
*
*@param \Exception $exception
*@return void
*/
public function report()
{
    if ($exception instance CustomException) {
        //
    }
    return parent::report($exception);
}

{tip}不要在report方法中进行太多的instanceof检查,而应该考虑使用[可报告异常(reportable exception)]
{(/docs/laravel/5.7/errors#renderable-exceptions)}。

Report辅助函数

有时你可能需要报告异常,但又不希望终止当前请求的处理。report辅助函数允许你使用异常处理器的report方法在不显示错误页面的情况下快速报告异常:

public function isValid()
{
    try{
    }catch(Exception $e){
        report($e);
        return false;
    }
}

按类型忽略异常

异常处理器的$dontReport属性包含一组不会被记录的异常类型。例如,由04错误导致的异常一级其他几种类型的错误不会写入日志文件。你可以根据需要添加其他异常类型到此数组中:

/**
*不应被报告的异常类型清单。
*
*@var array
*/
protected $dontReport = [
    \Illuminate\Auth\AuthenticationException::class,
    \Illuminate\Auth\Access\AuthorizationException::class,
    \Symfony\Component\HttpKernel\Exception\HttpException::class,
    \Illuminate\DataBase\Eloquent\ModelNotFoundException::class,
    \Illuminate\Validation\ValidationException::class,
];
Render方法

Rendor方法负责将给定的异常转换为将被发送回浏览器的HTTP响应。默认值情况下,异常将传递给你生成响应的基类。不过,你可以按自己意愿检查异常类型或者返回自己的自定义响应:

/**
*将异常1转换为HTTP响应。

  • @param \Illuminate\Http\Request $request
    @param \Illuminate\Exception $exception
    @return \Illuminate\Http\Response
    /
    public funtion render($request, Exception $exception)
    {
    if ($exception instanceof CustomException) {
    return response()->view('errors.custom', [], 500);
    }

return parent::render($request, $exception);
}

Reportable & Renderable 异常

除了在异常处理器的report和render方法中检查异常类型,你还可以直接在定义异常上定义report和render方法。当定义了这些方法时,它们会被框架自动调用:

<?php
namespace App\Exceptions;
use Exception;
class RenderException exteds Exception
{
/**
* 报告异常

@return void
*/
public function report()
{
//
}

    /**
     * 转换异常为 HTTP 响应
     *
     * @param  \Illuminate\Http\Request
     * @return \Illuminate\Http\Response
     */
    public function render($request)
    {
        return response(...);
    }
}

HTTP异常

一些异常用描述产生自服务器的HTTP错误代码。例如,[页面未找到]错误(404),【未经授权的错误】(401),甚至可以是开发人员引起的500错误。你可以使用abort辅助函数从应用程序的任何地方生成这样的响应:

abort(404);

辅助函数abort会立即引发一个由异常处理器渲染的异常。你还可以选择性地提供响应文本:
abort(403, 'Unauthorized action.');

自定义HTTP错误页面

laravel可以轻松显示各种HTTP状态代码的自定义错误页面。例如,如果你希望自定义404HTTP状态码的错误页面,可以创建一个resources/views/errors/404.blade.php视图文件。该文件将被用于你的应用程序产生的所有404错误。此目录中的视图文件的命名应匹配它们对应的HTTP状态码。由abort函数引发的HTTPException实例将作为$exception变量传递给视图:

{{ $exception->getMessage() }}

日志

简介

为了帮助你更多的了解应用程序中到底发生了什么,laravel提供了强大的日志服务,允许你将日志消息,系统错误日志记录到文件,甚至使用slack通知到你的整个团队。

在laravel框架中,laravel使用monolog库,它为各种强大的日志处理提供支持。laravel使用配置这些处理程序变得简单,允许泥混合并匹配它们自定义的应用程序日志处理。

配置

所有的应用程序日志系统配置都位于config/logging.php配置文件中。这个文件允许你配置你的应用程序日志通信,
所以务必查看每个可用的通信及它们的选项。当然,我们将在下面回顾一些常用的选项。

默认情况下,laravel将使用stack去记录日志消息。stack通道被用来将多个日志通道聚合到一个单一的通道中。关于堆栈的更多信息,查看以下文档。

配置通道名称

默认强狂下,monolog使用与当前环境匹配的【通道名称】进行实例化,比如production或者local。要改变这个值,需添加一个name选项到你的通道配置中:

'stack' => [
    'driver' => 'stack',
    'name' => 'channel-name',
    'channels' => ['single', 'slack'],
];

可用的通道驱动

名称 描述
single 一个便于创建[多通道]通道的包装器
daily 单个文件或者基于日志通道的路径(StreamHandler)
slack 一个每天轮换的基于monolog驱动的RotatingFileHandler
syslog 一个基于monolog驱动的sysloghandler
errorlog 一个基于monolog驱动的errorlogHandler
monolog 一个可以任何支持monolog处理程序的monolog工厂驱动程序
custom 一个调用指定工厂创建通信通道的驱动程序

{tip}有关monolog和custom驱动,查看高级通道自定义

配置slack通道

slack通道需要url配置选项。这个URL应当与你为slack团队配置的一个incoming webhook相匹配。

构建日志栈

如前所述,stack驱动允许你将多个通道合并到一个单一日志通道中。为了说明如何使用日志栈,让我们看一个你可能在生产应用配置中看到的实例配置:

'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['syslog', 'slack'],
    ],
        
    'syslog' => [
        'driver' => 'slack',
        'url' => env('LOG_SLACK_WEBHOOK_URL'),
        'username' => 'Laravel Log',
        'emoji' => ':boom:',
        'level' => 'critical',
    ],
],

让我们剖析这个配置。首先,注意我们的stack通道通过它的Channels选项聚合其它两个通道:syslog和slack。因此,当记录日志消息时,这两个通道都有机会去记录日志消息。

日志级别

注意上面实例中在syslog和slack通道配置中存在的level配置选项。这个选项决定了一个消息必须被通道记录的最小[level]。monolog为laravel的日志服务提供了RFC-542规范中定义的所有日志级别:emergency,
alert,critical,error,warning,notice,info和debug。

因此,假设我们记录一个日志消息使用debug方法:

Log::debug('An informational message.');

根据我们的配置,syslog通道将写消息到系统日志;然而,由于错误消息不是critical或者这个级别之上,它将不被发送到slack。但是,如果我们记录一个emergency消息,它将被同时发送到系统日志和slack,因为emergency级别高于我们对两个通道最低级别的阈值:

Log::emergency('The system is down!');

记录日志消息

你可以通过Log外观类将信息写入到日志。如前所述,日志器提供在RPC 5424规范中定义的八个日志级别:

emergency,alert,critical,error,warning,notice,info和debug:

Log::emergency($message);
Log::alert($message);
Log::critical($message);
Log::error($mesage);
Log::warning($message);
Log::notice($message);
Log::info($message);
Log::debug($message);

因此,你可以调用这些方法中的任何一个去记录相应级别的一个日志消息。默认情况下,消息将写入到你的config/logging.php配置文件配置的默认日志通道中:
<?php
namespace App\Http\Controllers;

use App\User;
use Illuminate\Support\Facades\Log;
use App\Http\Controllers\Controllers;

class UserController extendx Controller
{
/**
* Show the profile for the given user.

@param int $id
* @return Response
*/
public function showProfile($id)
{
Log::info('Showing user profile for user: '.$id);

    return view('user.profile', ['user' => User::findOrFail($id)]);
}

}

记录日志到指定通道

有时候你可能希望将日志记录到非默认通道。你可以使用log facade中的channel方法,将日志记录到应用配置中存在的任何聚到:

Log::channel('stack')->info('Something happened!');

如果你想按需要创建多个渠道的日志堆栈,你可以使用stack方法:

Log::stack(['single', 'slack'])->info("Something happened!");

先进的monolog日志通道定制

自定义monolog日志通道

首先你可能需要完全配置monolog现有的通道。例如:你想要为现有通道自定义一个monolog formatterInterface实现。

首先,在频道配置文件中定义一个tap数组。tap数组应该该包含所需的类列表,这些类就是Monolog实例创建后需要自定义(或者开发)的类:

'single' => [
'driver' => 'single',
'tap' => [],],

事件

简介

lumen事件提供了简单的监听器实现,允许你订阅和监听事件,事件类通常被保存在app/Events目录下,而它们的侦听器被保存在app/Listeners目录下。

与laravel的差异

通常,lumen中的事件方法恰好与laravel全栈框架功能一致,所以,请阅读完整的laravel文档。lumen同样支持事件广播,它允许客户端的JavaScript监听服务器的事件。然而,这里还是有些差异值得谈论。

生成器

lumen中没有可以用来生成事件和监听器的命令,你可以通过简单赋值ExampleEvent或者ExampleListener文件来定义你自己的事件和监听器,这两个示例文件提供了每个事件和监听器的基础类结构。

注册事件或者监听器

像laravel框架一样,lumen应用内置的EventServiceProvider提供了一个注册所有事件监听器的地方。listen属性一个数组,它包含了所有的事件(键)和监听器(值)。所以,你可以根据应用程序的需要添加事件到这个数组:

/**
*应用程序的事件监听器映射。
*
*@var array
*/
protected $listen = [
    'App\Events\ExampleEvent => [
        'App\Listeners\ExampleListener',
    ],
];

触发事件

你可以使用event辅助函数或者event门面在lumen应用程序中触发事件。同样,这些函数的行为与laravel框架一致:

event(new ExampleEvent);

Event::dispatch(new ExampleEvent);

队列

简介

lumen的队列服务给不同的后端队列提供统的API。队列允许你延迟处理消失的任务,例如在远程服务器上执行任务直到更晚的时间,而同一时间你的应用程序可以快速的处理web应用程序的请求。

就像该框架的许多其他部分一样,lumen的队列服务跟laravel的队列服务功能相同。因此,如果要了解更多关于lumen的队列,则可以参阅laravel消息队列文档。

配置

队列配置选项对话都在.env文件中。

如果您想要完全自定义的配置,必须您将vendor/laravel/lumen-framework/config/queue.php文件完整的复制到你的项目目录根中config目录,并且要调整必要的配置。如果config目录不存在,则要创建。

驱动程序先决条件

数据库

使用为了database队列驱动程序,您将需要数据库表保存作业和失败情况:

Schema::create('jobs', function (Blueprint $table) {
    $table->bigIncrement('id');
    $table->string('queue');
    $table->longText('payload');
    $table->tinyInteger('attempts')->unsigned();
    $table->unsignedInteger('reserved_at')->nullable();
    $table->unsignedInteger('available_at');
    $table->unsignedInteger('created_at');
    $table->index(['queue', 'reserved_at']);
});


Schema::create('failed_jobs', function (Blueprint $table) {
    $table->increment('id');
    $table->text('connection');
    $table->text('queue');
    $table->longText('payload');
    $table->longText('exception');
    $table->timestamp('failed_at')->useCurrent();
});

Redis的

要想使用Redis队列驱动程序,需要先通过composer安装illuminate/redis(5.5.*)扩展包。然后再bootstrap/app.php文件中注册Illuminate\Redis\RedisServiceProvider;

其他队列驱动程序的依赖包

下面列出其他队列驱动程序所需要的依赖扩展包:
亚马逊SQS:aws/aws-sdk-php ~3.0
Beanstalked:pda/pheanstalk ~3.0

与laravel的差异

与框架的许多其他部分一样,lumen队列作业的功能与laravel的队列作业功能相同。因此,要了解lumen队列作业功能,请查看完整的laravel队列文档。

不过呢,我们现在讲讨论两个框架间的一些细微差异。首先,我们来谈谈lumen中如何生成队列作业。

生成器

lumen不包括用于自动创建新job类的生成器。因此你需要赋值框架所带的exampleJob类。这个类提供了每个job类共享的基本结构。examplejob所继承的job基类已包含所需的interactswithqueue,queueable和serializesmodels特性:

<?php

namespace App\Jobs;

class ExampleJob extendx Job
{
/**

  • 创建一个新的作业实例。
  • @return void
    */
    public function __construct()
    {
    //
    }

/**

  • 执行作业。
  • @return void
    */
    public function handle()
    {
    //
    }
    }
调度作业

再次重申,你应该查阅完整的laravel文档以获取有关调节队列作业的完整信息;和laravel框架一样,你可以使用dispatch函数从lumen应用程序中的任意位置调度作业:

dispatch(new ExampleJob);

当然,你也可以使用Queue facade。如果你选择使用facade,请务必在bootstrap/app.php文件中取消对$app->withFacades()调用的注释:

Queue::push(new ExampleJob);

服务容器:

简介

laravel的服务容器是一个管理类依赖和执行依赖注入的强力工具。依赖注入是个花俏的名字,事实上是指:类的依赖通过构造器或者在某些情况下通过【setter】方法【注入】。

与laravel的差异

lumen使用了与laravel框架相同的服务器。所以,你可以使用它们所有强大的功能。有关容易的完整文档,请阅读laravel容器文档。

获取服务容器

laravel\lumen\application实例是Illuminate\Container\Container的扩展,所以你可以当做服务容器来使用。
通常我们会在服务提供者注册我们的容器解析规则。当然,你可以使用bind、singleton’instance、以及容器提供的其他方法。请记住,所有这些方法都记录在laravel服务器容易文档中。
解析实例

想要从服务容器中解析实例,你可以在大部分的功能类里自动解析(依赖注入),如路由closure,控制器的构造方法、控制器方法、中间件、事件监听器,或者队列等。或者,你也可以在应用程序中的任何地方使用App函数来进行解析:

$instance = app(Something::class);

服务提供者

简介
服务提供者是所有lumen应用程序启动的中心所在。包括你自己的应用程序,以及所有的核心服务,都是服务提供者启动的。

但是,我们所说的启动值得是什么?一般而言,我们指的是注册事物,包括注册服务容易绑定、事件侦听器、中间件,甚至路由、服务提供者设置你的应用程序的中心所在。

若你打开lumen的bootstrap/app.php文件,你将会看到$app->register()方法的调用。你也许需要额外的调用来注册你的服务提供者。

编写服务提供者

所有的服务提供者都集成了illuminate\Support\ServiceProvider这个类。这个抽象类要求你在你的提供者上定义至少一个方法:register。在register方法内,你应该只需要将事物绑定到服务容器中。永远不要试图在register方法中注册任何事件侦听器,路由或者任何其他功能。

注册方法

如前面所讲,在register方法中,你只要将事物绑定到【服务容器中】。永远不要试图在register方法中注册任何事件侦听器、路由或者任何其它功能。否则,你有可能会以外的使用到尚未加载的服务提供者提供的服务。

现在,让我们来看一个基本的服务提供者代码:

<?php

namespace App\Providers;

use Riak\Connection;
use Illuminate\Support\ServiceProvider;

class RiakServiceProvider extends ServiceProvder
{
    /**
    *注册绑定到容器中
    *
    *@return void
    */
    public function register()
    {
        $this->app->singleton(Connection::class, function ($app) {
            return new Connection(config('riak'));
        });
    }
}

这个服务提供者自定义了一个register方法,并且用这个方法在服务容器中绑定了Riak\Connection的一个实例。如果你不是很了解服务容器的运行原理,请查看[its documentation]。

启动方法

那么,如果我们要在服务提供者当中注册一个视图组件呢?这应该在boot方法内完成。此方法在所有其他服务提供者都注册之后才能调用,也就意味着可以访问已经被框架注册的所有服务:

<?php
namespace App\Providers;

use Queue;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvier
{
    //其他服务提供者属性。。。
    /**
    *引导任何应用程序服务。
    *@return void
    */
    public function boot()
    {
        Queue::failing(function ($envent) {
            
        });
    }
}
注册服务提供者

所有的服务提供者在bootstrap/app.php文件中被注册。这个文件中包含对$app->register()方法调用。你也行需要额外的调用$app->register()来注册你的服务提供者。

测试

简介

lumen在创建时就已考虑到测试部分。事实上,lumen默认就支持用PHPunit来做测试,并为你的应用程序创建好了phpunit.xml文件。框架还提供了一些便利的辅助函数,让你可以更直观的测试应用程序的json响应。

测试环境

在运行测试时,lumen自动配置讲缓存驱动配置为array,意味着在测试的时候不会保存任何的缓存数据。

你可以随意创建其他必要的测试配置环境。testing的环境变量可以在phpunit.xml文件中进行修改;

定义和运行测试

要创建一个测试用例,直接将新的测试文件创建到tests文件夹下即可。测试文件必须继承TestCase。接着就可以像平常使用PHPunit一样来定义测试方法。要运行测试只需要在命令行上运行PHPunit命令即可:

<?php

class FooTest extends TestCase
{
    public function testSomethingIsTrue()
    {
        $this->assertTrue(true);
    }
}

注意:如果要在你的类自定义setUp方法,请确保调用了parent::setUp。

应用测试

lumen提供了一个非常好用的api,使用它用来向你的应用发起HTTP请求,并查看输出结果。

测试json api接口

lumen同样提供了几个测试用于测试json api接口和响应数据的助手。例如,get,post,put,patch和delete方法可以被用于发起各种HTTP请求方式,并且声明以json格式返回一个指定的数组:

<?php
    
class ExampleTest extends TestCase
{
    /**
    *一个简单的测试例子
    *
    *@return void
    */
    public function testBasicExample()
    {
        $this->json('POST', '/user', ['name' => 'Sally']);
            ->seeJson([
                'created' => true,
            ]);
    }
}

seeJson方法数组转换为json,并验证这个json片段发生在应用返回的整个json响应的任意位置。所以,即使在json响应中存在其他属性,只要指定的片段存在,这个测试任然会成功!

验证完全匹配的json

如果你想验证传入的数组是否与应用程序返回的json完全匹配,你可以用seeJsonEquals方法:
<?php

class ExampleTest extendx TestCase
{
    /**
    *一个简单的测试例子
    *
    *@return void
    */
    public function testBasicExample()
    {
        $this->post('/user', ['name' => 'Sally'])
            ->seeJsonEquals([
                'created' => true,
            ]);
    }
}

认证

actingAs辅助函数提供了简单的方式来让指定的用户认证为当前的用户:
<?php

class ExampeTest exntends TestCase
{
    public function testApplication()
    {
        $user = factory('App\User')->create();
        $this->actingAs($user)
            ->get('/user');
    }
}

自定义HTTP请求

如果你想要创建一个自定义的HTTP请求到应用程序上,并获取完整的 Illuminate\Http\Response对象,可以使用call方法:

public function testApplication()
{
    $response = $this->call('GET', '/');
    $this->assertEquals(200, $response->status());
}

如果你想构造POST,PUT,或者PATCH请求,可以在请求时传入一个数组作为请求参数,当然,你也可以在路由及控制器中通过请求实例来获取传过来的参数:

$response = $this->call('POST', '/user', ['name' => 'Taylor']);

使用数据库

为了使得测试使用了数据库的应用更加简便,lumen提供了各种有用的工具。首先,你可以使用seeInDatabase助手函数来断言数据库中是否存在给定条件的数据。例如,我们想要验证users表中的有一条email的值为Sally@example.com的记录,我们可以按如下操作:

public function testDatabse()
{
    //Make call to application...
    
    $this->seeInDatabase('users', ['email' => 'sally@foo.com']);
}

当然,seeInDatabase方法和类似的助手方法就是会为了方便使用。你也可以在测试中自由使用PHPunit的内置断言方法。

每次测试之后重置数据库

在每次测试后重置数据库时非常有必要的,这样之前的测试数据不会响应后面的测试。

使用迁移

有一个选择是每次测试之后回滚数据库,并且在下一次测试之前将其迁移。lumen提供了一个简单的DatabaseMigrations特性,它可以自动位您处理。简单的在您的测试类中运用这个特性如下:

<?php

use Laravel\Lumen\Testing\DatabaseMigrations;
use Laravel\Lumen\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
    use DatabaseMigrations;
    
    /**
    *A basic functional test example.
    *
    *@return void
    */
    public function testBasicExample()
    {
        $this->get('/foo');
    }
}

使用事务

另一个选择是将每一个测试用例包装在数据库事务中。撸们提供了便利的DatabaseTransactions特性,可以为您自动的执行这些:

<?php

use Laravel\Lumen\Testing\DatabaseMigrations;
use Laravel\Lumen\Testing\DatabaseTransactions;

class ExampleTest extends TestCase
{
use DatabaseTransactions;
/**
A basic functional test example


/
}

模型工厂

在测试时,在执行测试之前我们需要将少量的数据插入到数据库中很常见的。当创建这些数据的时候,lumen不会手动指定这些列的值。而是允许您使用“factories”为每个Eloquent models定义一组默认属性。首先,在您的应用中看看database/factories/ModelFactory.php文件。这个文件包含一个工厂定义:

$factory->define('App\User', function ($faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
    ];
});

在作为工厂定义的闭包中,您可以返回模型上所有属性的默认测试值。闭包将接收Faker PHP库的一个实例,它将允许您便利的生成各种随机数据以方便测试。

当然,您可以将您自己的额外的工厂添加到ModelFactory.php文件中。

多种工厂类型

有时您可能希望同一个Eloquent模型有多种工厂。例如,可能您会希望除了普通的用户之外还有管理员用户的工厂。您可能会使用defineAs方法定义这些工厂:

$factory->defineAs('App\User', 'admin', function ($faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'admin' => true,
    ];
});

您可以使用raw方法检索其基本属性,而不是复制基本用户工厂中的所有属性。一旦拥有这些属性,只需要使用您需要的任何附加值补充它们:

$factory->defineAs('App\Users', 'admin', function ($faker) use ($factory) {
    $user = $factory->raw('App\User');
    return array_merge($user, ['admin' => true]);
});

在测试中使用工厂

在工厂定以后,就可以在测试或者是数据库的填充文件中,通过全局的factory函数来生成模型实例。接着让我们先来看看几个创建模型的例子。首先我们会使用make方法创建模型,但不将他们保存至数据库:
public function testDatabase()
{
$user = factory('App\User')->make();
}

如果你想要写模型中的某些默认值,则可以传递一个包含数值的数组至make方法。只有指定的数值会被替换,其他剩余的数值则会安装工程指定的默认值来设置:

$user = factory('App\User')->make([
'name' => 'Abigail',]);

你还可以创建许多模型的集合或者创建给定类型的模型:

//Create three App\User isntance...

$users = factory('App\User', 3)->make();

//Create an App\User "admin" instance...
$user = factory('App\User', 'admin')->make();

//Create three App\User "admin" instances...
$users = factory("App\User", 'admin', 3)->make();

维持工厂模式
可以使用create方法创建模型实例,还可以使用save方法将数据保存到数据库:

public function testDatabase()
{
    $user = factory("App\User")->create();
    // Use model in tests...
}

同样,你也可以使用数组的方式使用create方法将数据写入模型

$user = factory('App\User')->create([
    'name' => 'abigail',
]);

添加关联至模型

你甚至可以保存多个模型到数据库上。在本例中,我们还会增加关联至我们所创建的模型。当使用create方法创建多个模型时,它会返回一个Eloquent集合实例,让你能使用集合提供的便利方法,例如each方法:
$users = factory('App\User', 3)
->create()
->each(function ($u) {
$u->posts()->save(factory('App\Posts')->make);
});

模拟
模拟事件
如果你大量地使用lumen的事件系统,你可能会希望在测试停止或者模拟某些事件。例如,如果你在测试你的注册功能,你可能不希望所有的UserRegistered事件被触发,因为它们会触发“欢迎”邮件的发送。

lumen提供了简单的expectsEvents方法,以验证预期的事件有没有被运行,可防止该事件的任何处理进程被进行:
<?php

class ExampleTest extends TestCase
{
    public function testUserRegistration()
    {
        $this->expectsEvents('App\Events\UserRegistered');
        //测试用户注册功能...
    }
}

如果你想阻止所有的事件处理程序运行,你可以使用withoutEvents方法:

<?php
class Example extends TestCase
{
    public function testUserRegistraction()
    {
        $this->withoutEvents();
        //Test user registraction code。。。
    }
}

模拟任务

有时你可能希望当请求发送至应用程序时,简单地对控制器所派送的任务进行测试。这么做能够让你隔离测试路由或者控制器,设置除了任务以外的逻辑。当然,在此之后你也可以在一个单独的测试案例中测试该任务。

lumen提供了一个简便的expectsJob方法,以验证预期的任务有没有被派送,但任务本身不会被运行:

<?php
class Example extends TestCase
{
    public function testPurchasePodcast()
    {
        $this->expectsJobs('App\Jobs\PurchasePodcast');
        //测试购买博客代码...
    }
}

注意:该方法只检测通过全局猪手函数dispatch或者路由或控制器中的$this->dispatch方法派送的任务。它并不会检测被直接发送到Queue::push的任务。

模拟facades

在测试时,经常需要模拟对lumen facade的调用。例如,考虑如下控制器的操作:
<?php
namespace App\Http\Controllers;

use Cache;
class UserController extends Controller
{
/**
* 展示应用程序所有用户的列表。

@return Response
*/
public function index()
{
$value = Cache::get('key');

    //
}

}

我们可以使用shouldReceive方法来模拟调用Cache门面它会返回一个mockery模拟的实例。因为门面实际上已经被lumen的服务容器,解析和管理,它们比一般的静态类更具有可测性。例如,让我们模拟调用cache门面:
<?php
class FooTest extends TestCase
{
public function testGetIndex()
{
Cache::shouldReceive('get')
->once()
->with('key')
->andReturn('value');

        $this->get('/users');
    }
}

注意:你不应该模拟Request门面。应该在测试时使用如call及post这样的HTTP辅助函数来传递你想要的数据。

数据验证

简介

lumen提供了数种不同的方法来验证传入应用程序的数据。默认情况下,lumen的基本控制器类使用名为ProvidesConvenienceMethods的trait,其提供了一种便捷的方法来使用各种强大的验证规则验证传入的HTTP请求。

一般来说,lumen中的数据验证与laravel中的数据验证并无多大区别,因此你应该查过完整的laravel数据验证文档以熟悉其使用;不过,它们之间也存在少许重要的差异。

与laravel的差异

表单请求

lumen不支持表单请求。如果想使用表单请求,则赢使用完整的laravel框架。

$this->validate方法

在lumen中可用的$this->validate辅助方法将始终返回带有相关错误消息的json响应。而该方法的laravel版本,如果请求不是ajax请求,返回的则是重定向响应。由于lumen是无状态的,且不支持会话,所以闪存错误信息在会话中是不可能的。如果想要使用重定向及闪存错误数据,应该使用完整的laravel框架。

与laravel不同的是,lumen支持在Route闭包中访问validate方法:

use Illuminate\Http\Request;

$router->post('/user', function (Request $request) {
    $this->validate($request, [
        'name' => 'required',
        'email' => 'required|email|unique:users',
    ]);

    //存储用户
});

当然,你可以自由地使用Validator::make facade方法手动创建验证器实例,就像在laravel中一样。

exists和unique规则

如果想要使用exists或者unique验证规则,则应该在bootstrap/app.php文件中取消$app->withEloquent()方法调用的注释。

视图变量$errors

lumen不支持session,因此在laravel中每个视图都可用的$errors视图变量在lumen中式不可用的。如果验证失败,那么$this->validate辅助方法会抛出Illuminate\ValidationException异常,期中嵌入了包含所有相关错误消息的json响应,如果你并非只构建仅发送json响应的无状态api,则应使用完整的laravel框架。tongzhuo_examination_db

转载于:https://www.cnblogs.com/2018-05-9-ygk/p/10254709.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值