使用 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

在本系列的前兩部分中,介紹了設定套件的基本概念以及如何使用它在 Symfony 應用程式中建立良好的使用者可設定設定。
在本部分中,您將了解如何對設定進行版本控制並在它們之間進行遷移。此外,您還將學習如何將環境變數與設定結合。

版本控制和遷移

隨著時間的推移,您的應用程式將會不斷發展,您的設定也會不斷發展。這意味著隨著時間的推移,新參數將添加到設定中,舊參數將被刪除,現有參數將被更改。為了解決這個問題,settings-bundle 提供了版本控制和遷移機制,它可以為您處理大部分工作。

假設您有一個像這樣的簡單設定類別:

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-bundle 不知道如何將現有資料轉換為新結構。這就是遷移的地方
開始發揮作用。

您可以看到settings屬性現在指定了version選項和migrationService選項:

版本選項指定設定的最新架構版本,且只是一個整數(大於零),每次變更設定類別的結構時都會遞增。您可以從 1 開始,並在每次更改設定類別的結構時遞增它。您可以將版本號碼直接放入屬性中,也可以為其定義常數,如範例所示,這樣做的好處是您可以輕鬆地從類別外部檢索當前版本。

第二個新東西是migrationService選項。這指定了實際執行資料遷移的服務類別。 migrationService 必須實作 SettingsMigrationInterface,它指定一個遷移函數,負責在兩個給定的資料版本之間執行遷移。

在大多數情況下,您希望在版本之間逐步遷移(意味著您遷移 1 -> 2,然後遷移 2 -> 3 等等,而不是直接遷移 1 -> 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;
    }

}
登入後複製

遷移服務包含 migrateToVersionXX() 形式的各種方法,如果設定從版本 XX-1 遷移到版本 XX,類別會自動呼叫這些方法。此方法接收規範化形式的數據和設定類別的元數據,並且必須傳回規範化形式的數據,然後將其傳遞給參數類型轉換器。如果您想明確指定為哪個版本呼叫哪些函數,您可以重寫resolveStepHandler方法,該方法傳回要用於給定版本的閉包。

由於現有資料還沒有版本,所以假設是版本0。因此,當遇到這些資料settings-bundle時,會呼叫migrateToVersion1處理程序從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中文網其他相關文章!

來源:dev.to
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!