基于 Laravel 开发过程中,ThinkSNS+ 是如何做到 Laravel 配置可以网站后台配置的。
距离上一次分享差不多一周了,这篇文章,就分享下利用 Laravel 的 Bootstrapping
达到网站后台设置 laravel 配置。
需求场景
首先,ThinkSNS+ 作为一个用户可以使用的「社交系统」和开源网站程序一样拥有后台,那有一些配置,Laravel 是要求写在 /config/*.php
的配置文件中的,例如 app.name
、app.debug
等信息的配置,以及 Jobs 的驱动配置,广播系统的配置等,我们都搬到了网站后台,用户安装后可以不用修改配置文件的情况下镜像配置。
如何覆盖配置
我们首先打开 Illuminate\Foundation\Application::bootstrapWith
方法,代码如下:
/**
* Run the given array of bootstrap classes.
*
* @param array $bootstrappers
* @return void
*/
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;
foreach ($bootstrappers as $bootstrapper) {
$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
$this->make($bootstrapper)->bootstrap($this);
$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
}
}
重点代码在 $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
和 $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
上,很明显是加载并运行 bootstrapper 的前置和后置事件。
所以,我们看还有一个方法叫做 beforeBootstrapping
和 afterBootstrapping
然后怎么做呢?我们看
// Illuminate\Foundation\Http::$bootstrappers
/**
* The bootstrap classes for the application.
*
* @var array
*/
protected $bootstrappers = [
\Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
\Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
\Illuminate\Foundation\Bootstrap\HandleExceptions::class,
\Illuminate\Foundation\Bootstrap\RegisterFacades::class,
\Illuminate\Foundation\Bootstrap\RegisterProviders::class,
\Illuminate\Foundation\Bootstrap\BootProviders::class,
];
没错,这里是固定了顺序的,我错误的加载顺序,会造成laravel的失败,所以,我们选择在 之前继承 Illuminate\Foundation\Application
的 应用基础上增加一个事件,代码如下:
public function __construct($basePath = null)
{
parent::__construct($basePath);
// Load configuration after.
$this->afterBootstrapping(\Illuminate\Foundation\Bootstrap\LoadConfiguration::class, function ($app) {
$app->make(\Zhiyi\Plus\Bootstrap\LoadConfiguration::class)
->handle();
});
}
哪里添加的事件?
因为 ThinkSNS+ 是继承了 Illuminate\Foundation\Application
实现了新的 Application 类,所以我们直接在构造方法里面增加了代码。
这样,当 Laravel 启动,但是还没有加载 bootstrapper 的时候,已经把 加载配置的后置事件注入进去了。当加载配置执行完成后就会执行我注入的后置事件。
后置事件的实现
我们在创建了 \Zhiyi\Plus\Bootstrap\LoadConfiguration
这样一个类,注册为后置事件,路径为: /app/Bootstrap/LoadConfiguration.php
,然后实现代码如下:
<?php
namespace Zhiyi\Plus\Bootstrap;
use Zhiyi\Plus\Support\Configuration;
use Illuminate\Contracts\Foundation\Application;
class LoadConfiguration
{
protected $app;
protected $configuration;
/**
* 加载配置构造方法.
*
* @author Seven Du <shiweidu@outlook.com>
*/
public function __construct(Application $app, Configuration $configuration)
{
$this->app = $app;
$this->configuration = $configuration;
}
/**
* Run handler.
*
* @return void
* @author Seven Du <shiweidu@outlook.com>
*/
public function handle()
{
static $loaded = false;
if ($loaded) {
return;
}
$this->app->config->set(
$this->configuration->getConfigurationBase()
);
}
}
很简单,因为 app('config')
是一个 Illuminate\Contracts\Config\Repository
接口的实例,所以直接调用 set 方法进行配置覆盖。
而 Zhiyi\Plus\Support\Configuration
类是封装的自定义配置加载类,加载的配置文件存放在一个 YAML 文件中,这个类实现了 自定义配置文件的加载和保存。这样,我们从后台调用 API 然后 constroller 调用这个类的 save 方法进行持久化。
Zhiyi\Plus\Support\Configuration::getConfigurationBase
为什么要特殊说一下这个方法?因为这个方法的特殊性,也是 depth merge 实现的重要函数,在 Repository 中支持 app.name = value
这样的形式进行深曾键值赋值,利用这一个特性,这个函数将 多维数组转换为一维。
效果:
// 多维数组
$arr = [
'app' => [
'name' => 'xxx',
'env' = 'local'
]
];
// 转换后
$arr = [
'app.name' => 'xxx',
'app.env' => 'local',
];
然后调用 app('config')->set($arr)
就对 Laravel 的 config 进行了 depth merge。
最后,持久化保存的 YAML 内容如下:
app:
name: ThinkSNS+
queue:
default: database
cache:
default: memcached
stores:
memcached:
servers:
host: 127.0.0.1
port: 11211
所以,基于 depth merge 在 .plus.yml
配置中,只需要保存部分配置,即可不想配置结构的完整性的情况下对 Laravel 镜像配置合并。
ThinkSNS+ 仓库:https://github.com/zhiyicx/thinksns-plus
感谢大家的阅读,开源不易,请为我们点一个 Star ?
本作品采用《CC 协议》,转载必须注明作者和本文链接
真好 大神辛苦了~
先 mark 等下看
@pszhao 没事呀~
@Hexor 嗯嗯~
666,正好需要相关的资料