zzxworld

Laravel 框架在命令中调用控制器函数

Laravel 开发框架的控制器函数通常都是绑定到路由,然后以 HTTP 请求方式来调用。因为一个小小的想法作祟,我准备尝试一下在命令中直接调用控制器函数,结果比预想中要简单。

让我产生此想法的需求来自静态缓存功能。比较通用的静态缓存方案一般都是被动方式,比如访问者首次浏览页面时自动生成。而我想的是对于某些比较慢的页面,如果提前生成缓存页面,是不是能够「些微」提升一点用户的首次访问体验?所以我想实现这么一个主动缓存的功能。

在 Laravel 框架的命令中调用其他命令可以用 $this->call 方法,在控制器函数中调用命令可以用 Artisan::call 方法。但没有为命令中调用控制器函数提供任何便利的途径。也许这本身就是一个很小众,或者说是可有可无的需求吧。不过好在 Laravel 框架低耦合的设计模式让这件事做起来并不复杂。

无参数的控制器函数

无参数的控制器函数调用起来最简单,首先来看看这种。下面是我在命令中调用首页控制器函数的主要代码:

use App\Http\Controllers\HomeController;

public function handle()
{
    $view = (new HomeController)->index();
    $html = $view->render();
}

HomeController 是我的首页控制器,index 是处理首页业务逻辑的视图函数。通过直接实例化控制器对象,并调用 index 方法,可以得到一个 Illuminate\View\View 实例化对象。调用它的 render 方法能获取到模板渲染后的纯 HTML 代码,可以直接拿来作为缓存内容。

有参数的控制器函数

有参数的控制器函数调用起来会稍微麻烦一点。以我的文章详情页控制器函数为例:

/**
* 文章详情
*/
public function show(Request $request, $slug)
{
    // ...
}

它有两个必要的参数:$request$slug。前者是 Laravel 的请求对象,后者是定位文章的传参。调用这个函数前需要先准备好这两个参数。

use App\Http\Controllers\PostController;
use Illuminate\Http\Request;

public function handle()
{
    $request = new Request();
    $view = (new PostController)->show($request, 'about');
    $result = $view->render();
}

如果有更复杂的传参,可以在 new Request 对象时提供。它继承自 Symfony\Component\HttpFoundation\Request 对象,在实例化时可用的参数请参考如下源码:

/**
 * @param array    $query      The GET parameters
 * @param array    $request    The POST parameters
 * @param array    $attributes The request attributes
 *                             parameters parsed from the PATH_INFO, ...
 * @param array    $cookies    The COOKIE parameters
 * @param array    $files      The FILES parameters
 * @param array    $server     The SERVER parameters
 * @param string|resource|null $content    The raw body data
 */
public function __construct(
    array $query = [], 
    array $request = [], 
    array $attributes = [], 
    array $cookies = [], 
    array $files = [], 
    array $server = [], 
    $content = null
)
{
    // ...
}

一点点想法

虽然目前我只是想尝试一下主动缓存,不过这个功能延伸一下,也可以做点有意思的项目,比如带后台的「静态站点」引擎。想想可比那些依赖终端命令的纯静态站点引擎有意思多了。