基于 Laravel 开发过程中,ThinkSNS+ 是如何做到 Laravel 配置可以网站后台配置的。

距离上一次分享差不多一周了,这篇文章,就分享下利用 Laravel 的 Bootstrapping 达到网站后台设置 laravel 配置。

需求场景

首先,ThinkSNS+ 作为一个用户可以使用的「社交系统」和开源网站程序一样拥有后台,那有一些配置,Laravel 是要求写在 /config/*.php 的配置文件中的,例如 app.nameapp.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 的前置和后置事件。

所以,我们看还有一个方法叫做 beforeBootstrappingafterBootstrapping 然后怎么做呢?我们看

// 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 协议》,转载必须注明作者和本文链接
Seven 的代码太渣,欢迎关注我的新拓展包 medz/cors 解决 PHP 项目程序设置跨域需求。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 5

真好 大神辛苦了~

7年前 评论

先 mark 等下看

7年前 评论
medz

@pszhao 没事呀~

7年前 评论
medz

@Hexor 嗯嗯~

7年前 评论

666,正好需要相关的资料

7年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
创始人 @ Odore Inc.
文章
33
粉丝
201
喜欢
533
收藏
200
排名:23
访问:24.7 万
私信
所有博文
社区赞助商