jbtronics/settings-bundle を使用した Symfony アプリケーションのユーザー構成可能な設定 (パートの移行と環境変数)

WBOY
リリース: 2024-07-19 11:26:33
オリジナル
446 人が閲覧しました

User-configurable settings in Symfony applications with jbtronics/settings-bundle (Part  Migrations and environment variables

このシリーズの最初の 2 つのパートでは、設定バンドルの基本概念と、それを使用して Symfony アプリケーションでユーザーが構成可能な優れた設定を作成する方法を紹介しました。
このパートでは、設定をバージョン管理し、設定間を移行する方法を学びます。さらに、環境変数と設定を組み合わせる方法も学習します。

バージョン管理と移行

時間が経つにつれて、アプリケーションは進化し​​、設定も進化します。これは、時間の経過とともに新しいパラメータが設定に追加され、古いパラメータが削除され、既存のパラメータが変更されることを意味します。これに対処するために、設定バンドルは、ほとんどの作業を自動的に処理するバージョニングおよび移行メカニズムを提供します。

次のような単純な設定クラスがあると仮定しましょう:

namespace App\Settings;

use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;

#[Settings]
class TestSettings {

    #[SettingsParameter]
    public string $email = 'test@invalid';

    #[SettingsParameter]
    public int $baz = 42;
}
ログイン後にコピー

これらの設定はアプリケーションでしばらくの間すでに使用されており、ユーザーはすでにカスタマイズをアプリケーションに保存しています。新しいパラメーターを設定に追加したいだけの場合は、新しいプロパティをクラスに追加するだけで問題なく動作します。新しいパラメータはデフォルト値で初期化され、ユーザーは自由に変更できます:

#[Settings]
class TestSettings {

    #[SettingsParameter]
    public string $email = 'test@invalid';

    #[SettingsParameter]
    public int $baz = 42;

    #[SettingsParameter]
    public bool $qux = true;
}

ログイン後にコピー

パラメータの削除も同様に機能します。クラスからプロパティを削除すると、設定バンドルはそのプロパティの既存の値を無視し、次回設定が保存されるときに削除します。

ただし、さらに注意が必要なのは、フィールドの名前を変更する場合、またはさらに複雑な場合は、フィールドのタイプやデータの正確な保存方法を変更する場合です。ユーザーの既存のカスタマイズが失われないようにするには、設定の異なる表現間で変換する方法を指定する必要があります。設定バンドルは、移行用のフレームワークを提供することでこれをサポートできます。

複数の電子メール アドレスを持てるように設定クラスを変更するとします。また、baz パラメータのインデックスを 0 からではなく 1 から開始するように変更したいとします。つまり、既存のすべての値が 1 ずつ増加する必要があります。最終的に、設定クラスは次のようになります。

namespace App\Settings;

use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;

#[Settings(version: self::VERSION, migrationService: TestSettingsMigration::class)]
class TestSettings {

    public const VERSION = 1;

    #[SettingsParameter(type: ArrayType::class, options: ['type' => StringType::class])]
    public array $email = ['test@invalid'];

    #[SettingsParameter]
    //Now with different indexing
    public int $baz = 43;
}
ログイン後にコピー

テスト設定クラスは意図した新しい構造を持ち、アプリケーションで使用できるようになりました。ただし、設定バンドルは既存のデータを新しい構造に変換する方法を知りません。ここが移行の場所です

settings 属性に version オプションと migrationService オプションが指定されていることがわかります。

バージョン オプションは、設定の最新のスキーマ バージョンを指定します。これは単なる整数 (0 より大きい) であり、設定クラスの構造を変更するたびに増分されます。 1 から始めて、設定クラスの構造を変更するたびに値を増やすことができます。例に示すように、バージョン番号を属性に直接入力することも、その定数を定義することもできます。これには、クラスの外部から現在のバージョンを簡単に取得できるという利点があります。

2 番目の新しいことは、migrationService オプションです。これは、実際にデータ移行を実行するサービス クラスを指定します。 migrationService は、指定された 2 つのデータ バージョン間の移行を実行する移行関数を指定する、SettingsMigrationInterface を実装する必要があります。

ほとんどの場合、バージョン間で段階的に移行する必要があります (コードの重複を避けるために、1 -> 3 を直接移行するのではなく、1 -> 2、次に 2 -> 3 というように移行します)。この状況では、SettingsMigration クラスを拡張する方が簡単です。この抽象クラスを使用すると、移行サービスは次のようになります:

namespace App\Settings\Migrations;

use Jbtronics\SettingsBundle\Migrations\SettingsMigration;

class TestSettingsMigration extends SettingsMigration  {

    /**
     * This method is called automatically by the migration class and handles 
     * migration of version 0 (non versioned settings) to version 1.
     */
    public function migrateToVersion1(array $data, SettingsMetadata $metadata): array
    {

        /*
         * $data contains the old settings data, in the normalized form (in the way it was saved in the database)
         * Each key is the parameter name (not necessarily the property name) 
         * 
         * In the end we must return the new data in the normalized form, which is later then passed to 
         * the parameter type converters.
         */

        //If the email parameter was set, convert it to an array
        if (isset($data['email'])) {
            $data['email'] = [$data['email']];
        }

        //Increment the baz parameter, if it was set
        if (isset($data['baz'])) {
            $data['baz']++;
        }

        //Return the new data
        return $data;
    }

    /**
     * This method is called, to handle migration from version 1 to version 2.
     */
    public function migrateToVersion2(array $data, SettingsMetadata $metadata): array
    {
        //Perform some more migrations...

        return $data;
    }

}
ログイン後にコピー

移行サービスには、mergeToVersionXX() 形式のさまざまなメソッドが含まれており、設定がバージョン XX-1 からバージョン XX に移行される場合、クラスによって自動的に呼び出されます。このメソッドは、正規化された形式のデータと設定クラスのメタデータを受け取り、正規化された形式でデータを返す必要があります。その後、このデータはパラメーター型コンバーターに渡されます。どのバージョンでどの関数が呼び出されるかを明示的に指定したい場合は、特定のバージョンで使用するクロージャを返すresolveStepHandlerメソッドをオーバーライドできます。

既存のデータにはまだバージョンがないため、バージョン 0 であると想定されます。 したがって、これらのデータ設定が検出されると、バンドルは mitigToVersion1 ハンドラーを呼び出して、0 から最新のバージョン 1 に移行します。

The old data from the storage is passed to the migration method (as $data) and you have to convert it to the new form how it can be saved to storage and how the parameter type conversions can understand it. Each parameter is stored in the $data array with the parameter name as key. You can then modify the data as you like and return it in the end.

Please note that the $data array is in the normalized form, meaning that you only have simple datatypes like strings, integers, arrays and so on. If you want to like to work with the denormalized form (like objects, etc.) you might find the getAsPHPValue() and setAsPHPValue() methods available in the SettingsClass (or in the PHPValueConverterTrait) useful. Or you call the ParameterTypes you need directly.

The settings-bundle stores the version of the data in the storage provider, so that it is automatically known what version the data has and what migrations to perform. The migrations are automatically performed when trying to retrieve settings data (by getting the settings from the SettingsManager or calling a property of a lazy settings class). By default, the migrated data is written back to the storage after the migration, so that the migration only has to be performed once for each setting, even if the settings are not explicitly written back to the storage.

Environment variables

Environment variables are one of the classic possibilities to configure a Symfony application. They allow you for an easy configuration approach in automatic deployed applications, containers, etc. via a more or less unified interface. So they are pretty ideal for server administrators, who want to configure an application without touching the code. However, the big disadvantage of environment variables is, that they are not user-configurable, as users (even those intended as admin users) can not change them without direct access to the server.

To retain the advantages of environment variables, while also allowing users to configure the applications via the settings-bundle, the bundle can map environment variables to settings class parameters.

This is done via the envVar option on the SettingsParameter attribute:

#[Settings]
class TestSettings {

    #[SettingsParameter(envVar: 'APP_EMAIL')]
    public string $email = 'test@invalid';

    #[SettingsParameter(envVar: 'int:APP_BAZ', envVarMode: EnvVarMode::OVERWRITE)]
    public int $baz = 42;

    #[SettingsParameter(envVar: 'bool:APP_TEST_SETTINGS_QUX', envVarMode: EnvVarMode::OVERWRITE_PERSIST)]
    public bool $qux = true;
}
ログイン後にコピー

The envVar option specifies the environment variable to map to the parameter. If it does not exist, nothing happens. However, if it exists, the bundle will retrieve the value of the environment variable and set it as the value of the parameter. By default, the "raw" environment variable contains just a string. If you have another simple data type (like an integer or a boolean), you can use one of Symfony's env var processors to convert the string value of the env variable to the desired type (e.g. int:APP_BAZ, which converts the content of APP_BAZ to an int).

The environment variable handling happens transparently in the background, meaning that you can use the settings class as usual, and you (almost) do not have to care about the environment variables when using the settings.

Environment variable handling modes

The envVarMode option specifies how the environment variable should be handled. If no mode is specified, the mode EnvVarMode::INITIAL is used. In this mode the environment variable is only used to initialize the parameter. That means if the parameter is used the first time, instead of the default value in the code, the value of the environment variable is used. Users can change this value as they like, and the environment variable will not affect the parameter anymore. This mode allows a server administrator to set useful initial defaults via environment variables (e.g. while deploying the container), but users can change them completely later.

However, in some cases, you might want the server admin to enforce a certain value via environment variables and forbid users to change them via WebUI. For these cases, you can use the EnvVarMode::OVERWRITE and EnvVarMode::OVERWRITE_PERSIST mode. In this mode, the environment variable will always overwrite a parameter value, no matter what was set as a value before by users. This means that freshly retrieved settings will always have the value of the environment variable, even if the user changed it before. The OVERWRITE_PERSIST mode additionally writes the value back to the storage, so that the value is still set even after the env variable is removed (however users can then change the value again).

If a parameter is overwritten by an environment variable, its form field will be disabled in the default generated WebUI, so that users can see that the value is enforced by the environment variable and can not be changed via the WebUI.

A limitation of this system is that you can still change the value of a settings parameter in your code, even if it is overwritten by an environment variable. The changes will also be used in other parts of the application during the request. It is just that these changes do not get persisted, meaning that if you reload the settings from the storage, the value of the environment variable will be used again. If you try to change settings parameters via direct access in you code, you might want to check if the parameter is overwritten by an environment variable (by using the isEnvVarOverwritten method of the SettingsManager), and if so, you might want to disable the possibility to change the parameter in your code.

Environment variables mapper

For many constellations, the type conversion via the env var processor works fine. However, in some cases where you have more complex parameter types, you need a more complex conversion logic. For these cases, you can use the envVarMapper option of the SettingsParameter attribute. This option specifies a callable, which is called with the value of the environment variable and must return the value to set as the parameter value:

class TestSettings {

  #[SettingsParameter(envVar: 'string:ENV_VAR3', envVarMapper: [self::class, 'mapDateTimeEnv'])
  private ?\DateTime $dateTimeParam = null;

  public static function mapDateTimeEnv(?string $value): ?\DateTime
  {
    return $value ? new \DateTime($value) : null;
  }
}

ログイン後にコピー

The $value parameter passed, is the value retrieved from the environment variable, with env var processors applied, meaning that it not necessarily has to be a string.

Conclusion

You can see that jbtronics/settings-bundle can support you with handling changes in the schema of settings, and how to map environment variables to settings parameters. This allows you to have a flexible configuration system, which can be used by users and server administrators alike.

As always you can find more information in the bundle documentation.

以上がjbtronics/settings-bundle を使用した Symfony アプリケーションのユーザー構成可能な設定 (パートの移行と環境変数)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!