Paramètres configurables par l'utilisateur dans les applications Symfony avec jbtronics/settings-bundle (migrations de pièces et variables d'environnement

WBOY
Libérer: 2024-07-19 11:26:33
original
536 Les gens l'ont consulté

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

Dans les deux premières parties de cette série, les concepts de base du bundle de paramètres ont été introduits et comment l'utiliser pour créer de jolis paramètres configurables par l'utilisateur dans les applications Symfony.
Dans cette partie, vous apprendrez comment versionner vos paramètres et migrer entre eux. De plus, vous apprendrez à combiner des variables d'environnement avec des paramètres.

Gestion des versions et migration

Au fil du temps, votre application évoluera et vos paramètres également. Cela signifie qu'au fil du temps, de nouveaux paramètres seront ajoutés aux paramètres, les anciens seront supprimés et ceux existants seront modifiés. Pour gérer cela, le bundle de paramètres fournit un mécanisme de gestion des versions et de migration, qui s'occupe de la plupart du travail à votre place.

Supposons que vous ayez une classe de paramètres simple comme celle-ci :

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;
}
Copier après la connexion

Ces paramètres étaient déjà utilisés depuis un certain temps dans votre application et les utilisateurs y avaient déjà enregistré leurs personnalisations. Si vous souhaitez simplement ajouter un nouveau paramètre aux paramètres, vous pouvez le faire en ajoutant simplement une nouvelle propriété à la classe, et cela fonctionnera correctement. Le nouveau paramètre sera initialisé avec la valeur par défaut et les utilisateurs pourront le modifier à leur guise :

#[Settings]
class TestSettings {

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

    #[SettingsParameter]
    public int $baz = 42;

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

Copier après la connexion

La suppression d'un paramètre fonctionne de la même manière. Si vous supprimez une propriété de la classe, le bundle de paramètres ignorera les valeurs existantes et la supprimera la prochaine fois que les paramètres seront enregistrés.

Cependant, ce qui est plus délicat, c'est si vous souhaitez renommer un champ ou, plus complexe encore, modifier son type ou la manière exacte dont les données sont enregistrées. Pour ne pas perdre les personnalisations existantes des utilisateurs, vous devez spécifier comment convertir entre les différentes représentations des paramètres. Le bundle de paramètres peut vous aider en fournissant un cadre pour les migrations.

Supposons que vous souhaitiez modifier votre classe de paramètres de manière à ce que vous puissiez désormais avoir plusieurs adresses e-mail. De plus, vous souhaitez modifier l'indexation du paramètre baz, afin qu'il ne commence pas à 0, mais à 1, ce qui signifie que toutes les valeurs existantes doivent être incrémentées de 1. Au final, votre classe de paramètres devrait ressembler à ceci :

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;
}
Copier après la connexion

La classe des paramètres de test a désormais la nouvelle structure prévue et peut être utilisée dans l'application. Cependant, le bundle de paramètres ne saura pas comment convertir les données existantes vers la nouvelle structure. C'est là que les migrations
entre en jeu.

Vous pouvez voir que l'attribut settings a maintenant l'option version et l'option migrationService spécifiées :

L'option version spécifie la version de schéma la plus récente des paramètres et est simplement un nombre entier (plus grand zéro), qui est incrémenté à chaque fois que vous modifiez la structure de la classe de paramètres. Vous pouvez commencer par 1 et l'incrémenter à chaque fois que vous modifiez la structure de la classe de paramètres. Vous pouvez mettre le numéro de version directement dans l'attribut, ou vous pouvez définir une constante pour celui-ci, comme indiqué dans l'exemple, ce qui présente l'avantage de pouvoir récupérer facilement la version actuelle depuis l'extérieur de la classe.

La deuxième nouveauté est l'option migrationService. Ceci spécifie la classe de service qui effectue réellement la migration des données. Le migrationService doit implémenter SettingsMigrationInterface, qui spécifie une fonction de migration chargée d'effectuer la migration entre deux versions données des données.

Dans la plupart des cas, vous souhaitez effectuer des migrations par étapes entre les versions (ce qui signifie que vous migrez 1 -> 2, puis 2 -> 3 et ainsi de suite, au lieu de 1 -> 3 directement pour éviter la duplication de code). Dans cette situation, il est plus facile d’étendre la classe SettingsMigration. En utilisant cette classe abstraite, votre service de migration pourrait ressembler à ceci :

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;
    }

}
Copier après la connexion

Le service de migration contient différentes méthodes sous la forme migrateToVersionXX(), qui sont appelées automatiquement par la classe si les paramètres sont migrés de la version XX-1 vers la version XX. La méthode reçoit les données sous forme normalisée et les métadonnées de la classe de paramètres et doit renvoyer les données sous forme normalisée, qui sont ensuite transmises aux convertisseurs de types de paramètres. Si vous souhaitez spécifier explicitement quelles fonctions sont appelées pour quelle version, vous pouvez remplacer la méthode solveStepHandler, qui renvoie la fermeture à utiliser pour une version donnée.

Comme les données existantes n'avaient pas encore de version, on suppose qu'il s'agissait de la version 0. Par conséquent, lorsqu'il rencontrera ces données, le bundle de paramètres appellera le gestionnaire migrateToVersion1 pour migrer de 0 vers la version 1 la plus récente.

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;
}
Copier après la connexion

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;
  }
}

Copier après la connexion

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.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

source:dev.to
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!