提供对在持久存储中管理的Laravel应用程序运行时配置的支持

1.4.2 2024-03-25 10:56 UTC

This package is auto-updated.

Last update: 2024-08-25 11:59:44 UTC


README

Laravel持久配置存储库


此扩展为Laravel引入了持久配置存储库。特别地,它的使用提供了对应用程序运行时配置的支持,从数据库中加载配置。

有关许可信息,请查看LICENSE文件。

Latest Stable Version Total Downloads Build Status

安装

安装此扩展的首选方式是通过composer

运行

php composer.phar require --prefer-dist illuminatech/config

或添加

"illuminatech/config": "*"

到您的composer.json文件的require部分。

使用方法

此扩展允许使用来自外部存储(如关系型数据库)的数据重新配置已创建的配置存储库。它提供了一个特殊的配置存储库类\Illuminatech\Config\PersistentRepository,该类封装任何给定的配置存储库,并添加了一层从持久存储中保存和恢复数据。

<?php

use Illuminate\Config\Repository;
use Illuminate\Support\Facades\App;
use Illuminatech\Config\PersistentRepository;
use Illuminatech\Config\StorageDb;

$sourceConfigRepository = new Repository([
    'foo' => [
        'name' => 'Foo',
    ],
    'bar' => [
        'enabled' => false,
    ],
    'other' => [
        'value' => 'Some',
    ],
]);

$storage = new StorageDb(App::make('db.connection'));

$persistentConfigRepository = (new PersistentRepository($sourceConfigRepository, $storage))
    ->setItems([
        'foo.name',
        'bar.enabled',
    ]);

echo $persistentConfigRepository->get('foo.name'); // returns value from database if present, otherwise the one from source repository, in this case - 'Foo'

echo $persistentConfigRepository->get('other.value'); // keys, which are not specified as "items" always remain intact, in this case - always return 'Some'

应通过\Illuminatech\Config\PersistentRepository::setItems()在持久存储中保存的配置数据。只有显式定义为“items”的键才会被存储或从持久存储中检索。源配置存储库中存在的任何其他数据将保持不变。

PersistentRepository完全装饰任何配置存储库,符合\Illuminate\Contracts\Config\Repository接口,并可以替代\Illuminate\Config\Repository实例。特别是这允许您通过\Illuminatech\Config\PersistentRepository实例替换常规Laravel配置,将数据库中的配置应用到整个应用程序。您可以在您的AppServiceProvider类中这样做。例如

<?php

namespace App\Providers;

use Illuminate\Contracts\Config\Repository;
use Illuminate\Support\ServiceProvider;
use Illuminatech\Config\PersistentRepository;
use Illuminatech\Config\StorageDb;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->app->extend('config', function (Repository $originConfig) {
            $storage = new StorageDb($this->app->make('db.connection'));

            $newConfig = (new PersistentRepository($originConfig, $storage))
                ->setItems([
                    'mail.contact.address' => [
                        'label' => __('Email address receiving contact messages'),
                        'rules' => ['sometimes', 'required', 'email'],
                    ],
                    // ...
                ]);

            return $newConfig;
        });

        // ...
    }
}

然后,每次您通过config()函数、\Illuminate\Support\Facades\Config外观或服务容器访问应用程序中的'config'服务时,您都将与\Illuminatech\Config\PersistentRepository实例交互,获取由数据库数据修改的值。

提示:使用此扩展,无需手动将任何数据放入实际存储(例如编写数据库迁移或填充) - 它将自动填充。如果存储中缺少特定项的值,它将简单地从封装的配置存储库中获取(例如,如“config/*.php”文件中定义的那样)。然而,某些存储在它们可以正常工作之前需要一些准备,例如创建数据库表。

注意:此扩展不提供内置的应用程序配置替代服务提供程序,因为它可能不适合特定的应用程序,而\Illuminatech\Config\PersistentRepository的使用并不局限于这项任务。然而,您可以使用\Illuminatech\Config\Providers\AbstractPersistentConfigServiceProvider类作为此类服务提供程序的脚手架。例如

<?php

namespace App\Providers;

use Illuminatech\Config\Providers\AbstractPersistentConfigServiceProvider;
use Illuminatech\Config\StorageContract;
use Illuminatech\Config\StorageDb;

class PersistentConfigServiceProvider extends AbstractPersistentConfigServiceProvider
{
    protected function storage(): StorageContract
    {
        return (new StorageDb($this->app->make('db.connection')));
    }

    protected function items(): array
    {
        return [
            'mail.contact.address' => [
                'label' => __('Email address receiving contact messages'),
                'rules' => ['sometimes', 'required', 'email'],
            ],
            // ...
        ];
    }
}

不要忘记在“config/app.php”的“providers”部分注册您的特定持久配置服务提供程序

<?php

return [
    // ...
    'providers' => [
        // ...
        App\Providers\PersistentConfigServiceProvider::class,
    ],
    // ...
];

您还可以按特定应用程序实体管理持久配置。例如:假设我们需要允许应用程序用户自定义其个人资料页面的外观,如更改颜色方案或启用/禁用侧边栏等。此类设置可以通过绑定到用户Eloquent模型的\Illuminatech\Config\PersistentRepository来管理。此类模型可能如下所示

<?php

namespace App\Models;

use Illuminate\Config\Repository;
use Illuminate\Database\Eloquent\Model;
use Illuminatech\Config\PersistentRepository;
use Illuminatech\Config\StorageDb;

class User extends Model
{
    /**
     * @var \Illuminatech\Config\PersistentRepository configuration repository specific to this model.
     */
    private $config;

    /**
     * Returns configuration associated with this particular model.
     *
     * @return \Illuminatech\Config\PersistentRepository config repository.
     */
    public function getConfig(): PersistentRepository
    {
        if ($this->config === null) {
            if (empty($this->id)) {
                throw new \InvalidArgumentException('Unable to get config for model without ID.');
            }
    
            $repository = new Repository($this->defaultConfigData());
    
            $storage = (new StorageDb($this->getConnection()))
                ->setFilter(['user_id' => $this->id]); // ensure configuration varies per each model
    
            $this->config = (new PersistentRepository($repository, $storage))
                ->setItems($this->persistentConfigItems());
        }
    
        return $this->config;
    }
    
    /**
     * Defines default configuration for the model instance.
     *
     * @return array config.
     */
    private function defaultConfigData()
    {
        return [
            'sidebar' => [
                'enabled' => true,
            ],
            'color' => [
                'primary' => '#4099de',
                'sidebar' => '#b3c1d1',
            ],
        ];
    }
    
    /**
     * Defines the config items, which should be manageable from web interface and stored in the database.
     *
     * @return array config items.
     */
    private function persistentConfigItems(): array
    {
        return [
            'sidebar.enabled' => [
                'label' => 'Sidebar enabled',
                'rules' => ['sometimes', 'required', 'boolean'],
            ],
            'color.primary' => [
                'label' => 'Primary color',
                'rules' => ['sometimes', 'required', 'string'],
            ],
            'color.sidebar' => [
                'label' => 'Sidebar color',
                'rules' => ['sometimes', 'required', 'string'],
            ],
        ];
    }
}

这将允许您分别对每个用户记录操作持久化配置,因此配置页面结构可能如下所示

@php
/* @var $user App\Models\User */ 
@endphp
@extends('layouts.main')

@section('content')
@if ($user->getConfig()->get('sidebar.enabled'))
    @include('includes.sidebar', ['color' => $user->getConfig()->get('color.sidebar')])
@endif
<div style="background-color:{{ $user->getConfig()->get('color.primary') }};">
    ...
</div>
@endsection

配置项目规范

应该保存到持久存储的配置部分由 \Illuminatech\Config\PersistentRepository::setItems() 定义,它接受一个 \Illuminatech\Config\Item 列表或配置数组。每个配置项都应该定义一个键,该键指向源存储库中的目标值。配置项还具有一些属性,支持创建配置设置的网页界面。这些包括

  • 'id' - 字符串,列表中项目的唯一ID,此值将用于请求字段和表单输入。
  • 'label' - 字符串,配置值输入的详细标签。
  • 'hint' - 字符串,配置值或输入提示的详细描述。
  • 'rules' - 数组,值验证规则。
  • 'cast' - 字符串,要转换到的值的原生类型。
  • 'encrypt' - 布尔值,是否对存储中的值进行加密。
  • 'options' - 数组,项目的附加描述性选项,可以根据需要进行使用。

由于“key”是必填项,因此可以通过定义此键的单个字符串指定项。

以下是项目规范的示例

<?php

use Illuminatech\Config\Item;
use Illuminatech\Config\PersistentRepository;

$persistentConfigRepository = (new PersistentRepository(...))
    ->setItems([
        'some.config.value',
        'another.config.value' => [
            'label' => 'Custom label',
            'rules' => ['required', 'numeric'],
        ],
        [
            'key' => 'array.config.value',
            'rules' => ['required', 'array'],
            'cast' => 'array',
        ],
        new Item(['key' => 'explicit.object']),
    ]);

配置存储

声明的配置项可以保存到持久存储中,然后从中检索。实际的项目存储可以是任何符合 \Illuminatech\Config\StorageContract 接口类的类。

此扩展中可用以下存储

请参阅特定存储类的详细信息。

保存和恢复数据

\Illuminatech\Config\PersistentRepository 将在第一次尝试从它获取配置值时自动从持久存储中检索配置项值。

<?php

use Illuminatech\Config\PersistentRepository;

$persistentConfigRepository = (new PersistentRepository(...))
    ->setItems([
        'some.config',
    ]);

$value = $persistentConfigRepository->get('some.config'); // loads data from persistent storage automatically.

您也可以使用 restore() 方法手动从持久存储中获取数据

<?php

use Illuminatech\Config\PersistentRepository;

$persistentConfigRepository = (new PersistentRepository(...))
    ->setItems([
        'some.config',
    ]);

$persistentConfigRepository->restore(); // loads/re-loads data from persistent storage

注意! 任何在值恢复过程中出现的错误或异常都将自动抑制。这样做是为了避免在存储尚未准备好使用的情况下阻止应用程序,例如:数据库表尚不存在。存储失败错误仅会在应用程序日志中显示。您应该手动测试值恢复是否在您的应用程序中正常工作,以避免意外行为。

要保存配置数据到持久存储,请使用方法 save()

<?php

use Illuminatech\Config\PersistentRepository;

$persistentConfigRepository = (new PersistentRepository(...))
    ->setItems([
        'some.config',
        'another.config',
    ]);

$persistentConfigRepository->save([
    'some.config' => 'some persistent value',
    'another.config' => 'another persistent value',
]);

通过常规配置存储库接口(例如,通过方法 set()push() 等)进行的更改将不会自动保存到持久存储中。但是,您可以使用 synchronize() 方法将当前的配置项值保存到其中。

<?php

use Illuminatech\Config\PersistentRepository;

$persistentConfigRepository = (new PersistentRepository(...))
    ->setItems([
        'some.config',
        'another.config',
    ]);

$persistentConfigRepository->set('some.config', 'new value'); // no changes at the persistent storage at this point

$persistentConfigRepository->synchronize(); // save values to the persistent storage

提示:您可以在应用程序的 终止阶段 调用 synchronize(),确保保存应用程序运行期间所做的所有更改。

方法 reset() 清除保存到持久存储中的所有数据,恢复原始(例如默认)配置存储库值。

<?php

use Illuminate\Config\Repository;
use Illuminatech\Config\PersistentRepository;

$sourceConfigRepository = new Repository([
    'some' => [
        'config' => 'original value',
    ],
]);

$persistentConfigRepository = (new PersistentRepository($sourceConfigRepository, ...))
    ->setItems([
        'some.config',
    ]);

$persistentConfigRepository->save([
    'some.config' => 'new value',
]);

echo $persistentConfigRepository->get('some.config'); // outputs 'new value'

$persistentConfigRepository->reset(); // clears data in the persistent storage

echo $persistentConfigRepository->get('some.config'); // outputs 'original value'

您还可以使用 resetValue() 方法仅重置特定的配置键。

缓存

您可以使用兼容PSR-16的缓存存储来提高从持久化存储中检索配置项的性能。例如

<?php

use Illuminate\Config\Repository;
use Illuminate\Support\Facades\App;
use Illuminatech\Config\PersistentRepository;

$sourceConfigRepository = new Repository([
    'some' => [
        'config' => 'original value',
    ],
]);

$persistentConfigRepository = (new PersistentRepository($sourceConfigRepository, ...))
    ->setItems([
        'some.config',
    ])
    ->setCache(App::make('cache.store'))
    ->setCacheKey('global-config')
    ->setCacheTtl(3600 * 24);

验证

每个配置项都带有验证规则,默认匹配['sometimes' ,'required']。您可以在配置保存之前使用这些规则轻松地为用户输入创建验证,或者使用\Illuminatech\Config\PersistentRepository::validate()。例如

<?php

/* @var $request Illuminate\Http\Request */
/* @var $config Illuminatech\Config\PersistentRepository */

$validatedData = $config->validate($request->all()); // throws \Illuminate\Validation\ValidationException if validation fails.
// ...

您还可以使用\Illuminatech\Config\PersistentRepository::makeValidator()方法创建用于手动处理的验证器实例。

注意!如果在输入中使用点符号('.')而没有使用\Illuminatech\Config\PersistentRepository::validate()方法,请留意。默认情况下,Laravel将点视为验证规则中嵌套键的分隔符。您应该使用反斜杠('\')前缀它们,或者在\Illuminatech\Config\Item::$id中手动定义,以确保它不包含点。

创建配置网页界面

此扩展最常见的一个用途是创建一个网页界面,允许在运行时控制应用程序的配置。\Illuminatech\Config\PersistentRepository不仅用于应用配置,还帮助创建配置编辑的界面。

配置管理的Web控制器可能如下所示

<?php

namespace App\Http\Controllers;

use Illuminate\Contracts\Container\Container;
use Illuminate\Http\Request;

class ConfigController extends Controller
{
    /**
     * @var \Illuminatech\Config\PersistentRepository persistent config repository, which is set at `AppServiceProvider`.
     */
    private $config;
    
    public function __construct(Container $app)
    {
        $this->config = $app->get('config');
    }
    
    public function index()
    {
        $this->config->restore(); // ensure config values restored from database
        
        return view('config.form', ['items' => $this->config->getItems()]);
    }
    
    public function update(Request $request)
    {
        $validatedData = $this->config->validate($request->all());
    
        $this->config->save($validatedData);
    
        return back()->with('status', 'success');
    }
    
    public function restoreDefaults()
    {
        $this->config->reset();
        
        return back()->with('status', 'success');
    }
}

您可以在HTML表单输入组合期间操作\Illuminatech\Config\Item接口。例如

...
<form ...>
...
@foreach ($items as $item)
    <label>{{ $item->label }}</label>
    <input type="text" name="{{ $item->id }}" value="{{ $item->getValue() }}">
    <p>{{ $item->hint }}</p>
@endforeach
...
</form>
...

提示:您可以使用\Illuminatech\Config\Item::$options来设置动态表单输入的配置,在其中指定输入类型、CSS类等。

注意!记住,PHP在解析本地的'POST'请求时,会自动替换请求键中的非字母数字字符,如点('.'),破折号('-')等,这使得对如'config.some-key'这样的键进行集合和验证变得不可能。如果您打算通过常规'POST'请求提交值,则需要手动为每个持久化配置项设置\Illuminatech\Config\Item::$id值。例如

<?php

use Illuminatech\Config\PersistentRepository;

$persistentConfigRepository = (new PersistentRepository(...))
    ->setItems([
        'some.config.value' => [
            'id' => 'some_config_value',
        ],
        'another-config-value' => [
            'id' => 'another_config_value',
        ],
        // ...
    ]);

提示:如果您通过JSON格式或本地的(非欺骗性的)'PUT'请求提交配置项值,则不会遇到这个问题。

如果您正在使用Laravel Nova作为您的应用程序管理面板,您可以使用illuminatech/nova-config扩展轻松创建应用程序配置设置界面。

类型转换

您可以将复杂数据类型(如数组)作为持久数据操作。要实现这一点,您应该在\Illuminatech\Config\Item::$cast中指定配置项的类型转换。例如

<?php

use Illuminate\Config\Repository;
use Illuminatech\Config\PersistentRepository;

$sourceConfigRepository = new Repository([
    'some' => [
        'array' => ['one', 'two', 'three'],
    ],
]);

$persistentConfigRepository = (new PersistentRepository($sourceConfigRepository, ...))
    ->setItems([
        'some.array' => [
            'cast' => 'array', // cast value from persistent storage to array
            'rules' => ['sometimes', 'required', 'array'],
        ],
    ]);

$persistentConfigRepository->save([
    'some.array' => ['five', 'six'],
]);

$persistentConfigRepository->restore();

var_dump($persistentConfigRepository->get('some.array') === ['five', 'six']); // outputs 'true'

加密

如果您打算操作敏感数据,如密码、API密钥等,您可能希望将它们作为加密字符串存储,而不是明文。这可以通过启用\Illuminatech\Config\Item::$encrypt来实现。例如

<?php

use Illuminate\Config\Repository;
use Illuminatech\Config\PersistentRepository;

$sourceConfigRepository = new Repository([
    'some' => [
        'apiKey' => 'secret',
    ],
]);

$persistentConfigRepository = (new PersistentRepository($sourceConfigRepository, ...))
    ->setItems([
        'some.apiKey' => [
            'encrypt' => true, // encrypt value before placing it into the persistent storage
        ],
    ]);

注意,数据加密会影响配置存储库的性能。

垃圾回收

随着您的项目发展,新的配置项可能会出现,一些也可能变得不再需要。\Illuminatech\Config\PersistentRepositorysetItems()设置没有匹配的配置项时,会自动忽略持久存储中的任何值。因此,存储的过时值无论如何都不会影响配置存储库,但它们仍然可能占用存储空间中的额外空间。您可以使用gc()方法手动从存储中删除所有过时值。

<?php

use Illuminate\Config\Repository;
use Illuminatech\Config\PersistentRepository;
use Illuminatech\Config\StorageDb;

$sourceConfigRepository = new Repository([
    'some' => [
        'config' => 'original value',
    ],
]);

$storage = new StorageDb(...);
$storage->save([
    'some.config' => 'some value',
    'obsolete.config' => 'obsolete value',
]);

$persistentConfigRepository = (new PersistentRepository($sourceConfigRepository, $storage))
    ->setItems([
        'some.config',
    ]);

$persistentConfigRepository->gc(); // removes 'obsolete.config' from storage

如果启用了Illuminatech\Config\PersistentRepository::$gcEnabled,则每次通过save()synchronize()方法保存配置值时,将自动执行垃圾回收。