web-dev-qa-db-fra.com

Yii2: Comment définir les valeurs d'attribut par défaut dans ActiveRecord?

Cela peut sembler une question triviale, mais toutes les solutions évidentes auxquelles je peux penser ont leurs propres défauts.

Nous voulons pouvoir définir n'importe quelle valeur d'attribut ActiveRecord par défaut pour les nouveaux enregistrements uniquement, de manière à ce qu'il soit lisible avant et pendant la validation et n'interfère pas avec les classes dérivées utilisées pour la recherche.

Les valeurs par défaut doivent être définies et prêtes dès l'instanciation de la classe, de sorte que (new MyModel)->attr renvoie la valeur par défaut attr.

Voici certaines des possibilités et des problèmes qu’ils rencontrent:

  • A) Dans MyModel, remplacez la méthode init() et affectez la valeur par défaut lorsque isNewRecord est vrai, comme ceci:

    public function init() {
        if ($this->isNewRecord) {
            $this->attr = 'defaultValue';
        }
        parent::init();
    }
    

    Problème: Recherche. Sauf si nous désactivons explicitement notre attribut par défaut dans MySearchModel (très sujet aux erreurs car il est trop facile de l'oublier), cela va également définir la valeur avant d'appeler search() dans la classe dérivée MySearchModel et gêner la recherche (l'attribut attr sera déjà défini de manière la recherche renverra des résultats incorrects). Dans Yii1.1, cela a été résolu en appelant unsetAttributes() avant d’appeler search(); cependant, une telle méthode n’existe pas dans Yii2.

  • B) Dans MyModel, remplacez la méthode beforeSave() comme suit:

    public function beforeSave($insert) {
        if ($insert) {
            $this->attr = 'defaultValue';
        }
        return parent::beforeSave();
    }
    

    Problème: l'attribut n'est pas défini dans les enregistrements non sauvegardés. (new MyModel)->attr est null. Pire encore, même les autres règles de validation qui reposent sur cette valeur ne pourront pas y accéder, car beforeSave() est appelé after validation. 

  • C) Pour que la valeur soit disponible lors de la validation, nous pouvons remplacer la méthode beforeValidate() et y définir les valeurs par défaut, comme suit:

    public function beforeValidate() {
        if ($this->isNewRecord) {
            $this->attr = 'defaultValue';
        }
        return parent::beforeValidate();
    }
    

    Problème: l'attribut n'est toujours pas défini dans les enregistrements non sauvegardés (non validés). Nous devons au moins appeler $model->validate() si nous voulons obtenir la valeur par défaut.

  • D) Utilisez DefaultValidator in rules() pour définir une valeur d'attribut par défaut lors de la validation, comme suit:

    public function rules() {
        return [
            [
                'attr', 'default',
                'value' => 'defaultValue',
                'on' => 'insert', // instantiate model with this scenario
            ],
            // ...
        ];
    }
    

    Problème: Identique à B) et C). La valeur n'est définie que lorsque nous avons sauvegardé ou validé l'enregistrement.

Alors, quelle est la bonne façon de définir les valeurs d'attribut par défaut? Existe-t-il une autre solution sans les problèmes décrits?

10
mae

Je sais que la réponse est trouvée, mais je vais ajouter mon approche . J'ai des modèles Application et ApplicationSearch. Dans le modèle d'application, j'ajoute init avec une vérification de l'instance actuelle. Si c'est ApplicationSearch, je saute les initialisations.

    public function init()
    { 
        if(!$this instanceof ApplicationSearch)  
        {
            $this->id = hash('sha256',  123);
        }

        parent::init();
    }

de plus, comme @mae a commenté ci-dessous, vous pouvez vérifier l'existence d'une méthode de recherche dans l'instance actuelle, en supposant que vous n'avez ajouté aucune méthode de recherche de nom au modèle de base autre que de recherche.

    public function init()
    { 
        // no search method is available in Gii generated Non search class
        if(!method_exists($this,'search'))  
        {
            $this->id = hash('sha256',  123);
        }

        parent::init();
    }
5
Stefano Mtangoo

Il y a deux façons de faire ça.

$model => new Model();

Maintenant, $model possède tous les attributs par défaut de la table de base de données.

Ou dans vos règles, vous pouvez utiliser:

[['field_name'], 'default', 'value'=> $defaultValue],

Maintenant, $model sera toujours créé avec les valeurs par défaut que vous avez spécifiées.

Vous pouvez voir la liste complète des validateurs principaux ici http://www.yiiframework.com/doc-2.0/guide-tutorial-core-validators.html

15
Coz

J'ai lu votre question à plusieurs reprises et je pense qu'il y a des contradictions.
Vous voulez que les valeurs par défaut soient lisibles avant et pendant la validation, puis vous essayez init() ou beforeSave(). Donc, en supposant que vous souhaitiez simplement définir les valeurs par défaut dans le modèle afin qu'elles puissent être présentes pendant la partie du cycle de vie le plus longtemps possible et ne pas interférer avec les classes dérivées, définissez-les simplement après l'initialisation de l'objet.

Vous pouvez préparer une méthode séparée où toutes les valeurs par défaut sont définies et l'appeler explicitement.

$model = new Model;
$model->setDefaultValues();

Ou vous pouvez créer une méthode statique pour créer un modèle avec toutes les valeurs par défaut définies et en renvoyer l'occurrence.

$model = Model::createNew();

Ou vous pouvez transmettre les valeurs par défaut au constructeur.

$model = new Model([
    'attribute1' => 'value1',
    'attribute2' => 'value2',
]);

Ce n'est pas très différent de définir les attributs directement.

$model = new Model;
$model->attribute1 = 'value1';
$model->attribute2 = 'value2';

Tout dépend de la transparence de votre modèle pour votre contrôleur.

De cette façon, les attributs sont définis pour tout le cycle de vie, à l'exception de l'initialisation directe, sans interférence avec le modèle de recherche dérivé.

1
Bizley

Remplacez simplement la méthode __construct() dans votre modèle comme ceci:

class MyModel extends \yii\db\ActiveRecord {

    function __construct(array $config = [])
       {
           parent::__construct($config);
           $this->attr = 'defaultValue';
       }
    ...
}