illuminatech / config
提供对在持久存储中管理的Laravel应用程序运行时配置的支持
Requires
- illuminate/support: ^9.0 || ^10.0 || ^11.0
Requires (Dev)
- illuminate/cache: *
- illuminate/config: *
- illuminate/database: *
- illuminate/encryption: *
- illuminate/validation: *
- phpunit/phpunit: ^8.0 || ^9.3 || ^10.5
- psr/log: ^1.0
README
Laravel持久配置存储库
此扩展为Laravel引入了持久配置存储库。特别地,它的使用提供了对应用程序运行时配置的支持,从数据库中加载配置。
有关许可信息,请查看LICENSE文件。
安装
安装此扩展的首选方式是通过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\StorageDb - 在关系型数据库中存储配置;
- \Illuminatech\Config\StorageEloquent - 使用 Eloquent 模型存储配置;
- \Illuminatech\Config\StoragePhp - 在本地 PHP 文件中存储配置;
- \Illuminatech\Config\StorageArray - 在运行时内存中存储配置;
请参阅特定存储类的详细信息。
保存和恢复数据
\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\PersistentRepository
在setItems()
设置没有匹配的配置项时,会自动忽略持久存储中的任何值。因此,存储的过时值无论如何都不会影响配置存储库,但它们仍然可能占用存储空间中的额外空间。您可以使用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()
方法保存配置值时,将自动执行垃圾回收。