Valeur par défaut dans Doctrine

338

Comment définir une valeur par défaut dans Doctrine 2?

Jiew Meng
la source
26
@ORM \ Column (name = "foo", type = "decimal", precision = 7, scale = 2, options = {"default" = 0}) fonctionne (d'après la réponse non populaire ci-dessous)
WayFarer
2
@ORM \ Column (name = "is_activated", type = "boolean", options = {"default": 0}) OU @ORM \ Column (name = "is_activated", type = "boolean", options = {"default "= 0})
ahmed hamdy
Ahmed, cela ne semble pas fonctionner pour les booléens dans Symfony 2.3. Cependant options = {"default" = "0"}) fonctionne, mettant l'entier entre guillemets.
Acyra
4
Si c'est un booléen, pourquoi n'utilisez-vous pas: options = {"default": false}?
robocoder

Réponses:

385

Les valeurs par défaut de la base de données ne sont pas "portables" prises en charge. La seule façon d'utiliser les valeurs par défaut de la base de données consiste à utiliser l' columnDefinitionattribut de mappage dans lequel vous spécifiez l' SQLextrait de code ( DEFAULTcause incluse) pour la colonne à laquelle le champ est mappé.

Vous pouvez utiliser:

<?php
/**
 * @Entity
 */
class myEntity {
    /**
     * @var string
     *
     * @Column(name="myColumn", type="string", length="50")
     */
    private $myColumn = 'myDefaultValue';
    ...
}

Les valeurs par défaut au niveau PHP sont préférées car elles sont également correctement disponibles sur les objets nouvellement créés et persistants (Doctrine ne reviendra pas à la base de données après avoir persisté un nouvel objet pour obtenir les valeurs par défaut).

romanb
la source
11
mais il y a un problème ici: et si je mets un type "datetime"?
artragis
46
@artragis a mis votre instanciation dans le constructeur d'entité
Alain Tiemblo
16
Il faut faire attention aux migrations en utilisant cette approche car toutes les lignes existantes entraîneront l'échec de la migration.
Tamlyn
7
N'utilisez pas la zone d'instanciation pour définir des variables ... Croyez-moi, une mauvaise chose va arriver. Utilisez plutôt la zone constructeur.
mimoralea
4
Je recommande d'utiliser la columnDefinition dans l'annotation, ou quelqu'un utilisera le client mysql ou phpmyadmin et les valeurs seront erronées ...
NDM
542
<?php
/**
 * @Entity
 */
class myEntity {
    /**
     * @var string
     *
     * @ORM\Column(name="myColumn", type="integer", options={"default" : 0})
     */
    private $myColumn;
    ...
}

Notez que cela utilise SQL DEFAULT, qui n'est pas pris en charge pour certains champs comme BLOBet TEXT.

iv3ndy
la source
4
Bonne prise! Il semble qu'il n'y ait pas d'option options = {"default" = 0} dans la documentation officielle
WayFarer
2
Pour info, le optionsparamètre est également utile pour les unsignedvaleurs. voir cette réponse
yvoyer
5
J'utilise à la fois cela et la réponse acceptée pour couvrir toutes les bases. Également juste une note que vous pouvez également faire: faites options={"default": 0}attention à utiliser "et non", car cela provoque des erreurs dans ma version de la doctrine.
Scott Flack
28
Ce devrait être la réponse choisie!
Acelasi Eu
2
@Matt, il a probablement dit cela parce qu'il s'agissait d'une fonctionnalité non documentée et que les fonctionnalités non documentées sont susceptibles d'être supprimées. Comme il est documenté maintenant, vous devriez l'utiliser en toute sécurité.
JCM
62

Configurez un constructeur dans votre entité et définissez-y la valeur par défaut.

Jeremy Hicks
la source
Cela semble certainement l'approche logique. Quelqu'un at-il rencontré des problèmes avec la configuration des valeurs par défaut dans le constructeur?
cantera
26
Solution recommandée par Doctrine: doctrine-project.org/docs/orm/2.1/en/reference/faq.html
cantera
@ cantera25 qui devrait être la réponse +1
Phill Pafford
3
cela ne met pas à jour les entités existantes si vous ajoutez un nouveau champ qui doit avoir une valeur par défaut. donc non ça ne devrait pas être la réponse. dépend de ce que vous devez faire exactement
Tomáš Tibenský
Cela ne fonctionnera pas non plus pour la mise à jour. Si vous souhaitez revenir à la valeur par défaut en vidant simplement le champ (même pour un entier), malheureusement, cela ne fonctionnera pas.
ThEBiShOp
55

Utilisation:

options={"default":"foo bar"}

et pas:

options={"default"="foo bar"}

Par exemple:

/**
* @ORM\Column(name="foo", type="smallint", options={"default":0})
*/
private $foo
Stanislav Terletskyi
la source
2
Je suis désolé, tu as raison. Vous pouvez donc trouver une explication sur cette page: doctrine-orm annotations-reference
Stanislav Terletskyi
51

Mettre à jour

Une raison de plus pour laquelle la lecture de la documentation de Symfony ne sortira jamais de la tendance. Il existe une solution simple pour mon cas spécifique et consiste à définir l' field typeoption empty_datasur une valeur par défaut.

Encore une fois, cette solution est uniquement pour le scénario où une entrée vide dans un formulaire définit le champ DB sur null.

Contexte

Aucune des réponses précédentes ne m'a aidé avec mon scénario spécifique mais j'ai trouvé une solution.

J'avais un champ de formulaire qui devait se comporter comme suit:

  1. Non requis, peut être laissé en blanc. (Utilisé 'requis' => faux)
  2. Si laissé vide, il devrait par défaut avoir une valeur donnée. Pour une meilleure expérience utilisateur, je n'ai pas défini la valeur par défaut sur le champ de saisie, mais plutôt utilisé l'attribut html «placeholder» car il est moins envahissant.

J'ai ensuite essayé toutes les recommandations données ici. Permettez-moi de les énumérer:

  • Définissez une valeur par défaut lorsque pour la propriété d'entité:
<?php
/**
 * @Entity
 */
class myEntity {
    /**
     * @var string
     *
     * @Column(name="myColumn", type="string", length="50")
     */
    private $myColumn = 'myDefaultValue';
    ...
}
  • Utilisez l'annotation des options:
@ORM\Column(name="foo", options={"default":"foo bar"})
  • Définissez la valeur par défaut sur le constructeur:
/**
 * @Entity
 */
class myEntity {
    ...
    public function __construct()
    {
        $this->myColumn = 'myDefaultValue';
    }
    ...
}
Rien de tout cela n'a fonctionné et tout cela à cause de la façon dont Symfony utilise votre classe Entity.

IMPORTANT

Les champs de formulaire Symfony remplacent les valeurs par défaut définies sur la classe Entity. Cela signifie que votre schéma pour votre base de données peut avoir une valeur par défaut définie, mais si vous laissez un champ non obligatoire vide lors de la soumission de votre formulaire, l' form->handleRequest()intérieur de votre form->isValid()méthode remplacera ces valeurs par défaut sur votre Entityclasse et les définira sur les valeurs du champ d'entrée. Si les valeurs du champ de saisie sont vides, il définira la Entitypropriété sur null.

http://symfony.com/doc/current/book/forms.html#handling-form-submissions

Ma solution

Définissez la valeur par défaut sur votre contrôleur après l' form->handleRequest()intérieur de votre form->isValid()méthode:

...
if ($myEntity->getMyColumn() === null) {
    $myEntity->setMyColumn('myDefaultValue');
}
...

Pas une belle solution mais ça marche. Je pourrais probablement faire un validation groupmais il peut y avoir des gens qui voient ce problème comme une transformation de données plutôt que la validation de données , je vous laisse le soin de décider.


Remplacer le poseur (ne fonctionne pas)

J'ai également essayé de remplacer le Entitysetter de cette façon:

...
/**
 * Set myColumn
 *
 * @param string $myColumn
 *
 * @return myEntity
 */
public function setMyColumn($myColumn)
{
    $this->myColumn = ($myColumn === null || $myColumn === '') ? 'myDefaultValue' : $myColumn;

    return $this;
}
...

Cela, même s'il semble plus propre, cela ne fonctionne pas . La raison étant que la mauvaise form->handleRequest()méthode n'utilise pas les méthodes de définition du modèle pour mettre à jour les données (fouillez form->setData()pour plus de détails).

Callistino
la source
Cette réponse devrait aller en haut à coup sûr. Le composant de formulaire utilise PropertyAccessor pour obtenir et définir les valeurs de vos propriétés. Peut-être que l'accesseur de propriété devrait utiliser les méthodes lorsqu'elles sont disponibles?
Xobb
1
les colonnes booléennes ne supportent pas les valeurs par défaut de php, donc seules les annotations
Crusader
C'est la seule solution qui a fonctionné lorsque les informations proviennent de formulaires. Je suis également en désaccord avec les commentaires ci-dessus concernant les booléens. Ils n'acceptent pas l'annotation par défaut.
BernardA
Le composant de formulaire Symfony utilise des paramètres de modèle, mais uniquement lorsque les données de format de modèle du formulaire diffèrent des données renvoyées par le getter correspondant de l'instance d'objet de modèle. Si vous avez vos méthodes setter / getter personnalisées - utilisez l'option de formulaire "property_path" (sera gérée par PropertyAccessor) ou DataMapper personnalisé (permet de définir manuellement la routine de transfert de données entre le formulaire et l'objet modèle).
Arkemlar
1
Cette question concerne la doctrine, pas symfony, donc cette réponse n'est pas vraiment sur le sujet.
Omn
18

La solution de contournement que j'ai utilisée était a LifeCycleCallback. J'attends toujours de voir s'il y a plus de méthode "native", par exemple @Column(type="string", default="hello default value").

/**
 * @Entity @Table(name="posts") @HasLifeCycleCallbacks
 */
class Post implements Node, \Zend_Acl_Resource_Interface {

...

/**
 * @PrePersist
 */
function onPrePersist() {
    // set default date
    $this->dtPosted = date('Y-m-d H:m:s');
}
Jiew Meng
la source
1
Pour les futurs lecteurs, ne vous fiez pas aux rappels du cycle de vie :) même Marco Pivetta est contre eux.
emix
Avertissement! Si l'entité a déjà défini la propriété dtPosted, votre code remplacera simplement la propriété. Utilisez toujours des accesseurs s'ils existent! if (!$this->getDtPosted()) { $this->setDtPosted(new \DateTime()); }
Barh
13

Vous pouvez également le faire en utilisant xml:

<field name="acmeOne" type="string" column="acmeOne" length="36">
    <options>
        <option name="comment">Your SQL field comment goes here.</option>
        <option name="default">Default Value</option>
    </options>
</field>
Andrew Zhilin
la source
8

Voici comment je l'ai résolu par moi-même. Voici un exemple d'entité avec une valeur par défaut pour MySQL. Cependant, cela nécessite également la configuration d'un constructeur dans votre entité et pour que vous puissiez y définir la valeur par défaut.

Entity\Example:
  type: entity
  table: example
  fields:
    id:
      type: integer
      id: true
      generator:
        strategy: AUTO
    label:
      type: string
      columnDefinition: varchar(255) NOT NULL DEFAULT 'default_value' COMMENT 'This is column comment'
Putna
la source
Avec cette ligne dans ma configuration, Doctrine essaie de supprimer la valeur par défaut sur la colonne chaque fois que je cours. php app / console doctrine: schema: update
shapeshifter
1
C'est la pire réponse ici. columnDefinitionva directement contre le but d'avoir un ORM, qui est l'abstraction de la base de données. Cette solution rompra la portabilité, gardera votre logiciel dépendant de votre fournisseur de base de données et cassera également les outils de Doctrine Migrations.
Pedro Cordeiro
@PedroCordeiro Je suis entièrement d'accord avec vous. Ce n'est qu'une solution rapide jusqu'à ce qu'un autre problème survienne.
Putna
7

Fonctionne pour moi sur une base de données mysql également:

Entity\Entity_name:
    type: entity
    table: table_name
    fields: 
        field_name:
            type: integer
            nullable: true
            options:
                default: 1
Alex Hoang
la source
Au format d'annotation pour qui est intéressé: @ORM \ Column (name = "Entity_name", type = "integer", options = {"default" = "1"})
Hannes
7

Rien de tout cela n'a fonctionné pour moi. J'ai trouvé de la documentation sur le site de doctrine qui dit de définir la valeur directement pour définir une valeur par défaut.

https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/faq.html#how-can-i-add-default-values-to-a-column

private $default = 0;

Cela a inséré la valeur que je voulais.

metric152
la source
Veuillez modifier le lien vers doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/… Voir point 3.2.2. Comment puis-je ajouter des valeurs par défaut à une colonne?
Tobi
3

Ajout à @romanb brillante réponse.

Cela ajoute un peu de surcharge dans la migration, car vous ne pouvez évidemment pas créer un champ sans contrainte nulle et sans valeur par défaut.

// this up() migration is autogenerated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() != "postgresql");

//lets add property without not null contraint        
$this->addSql("ALTER TABLE tablename ADD property BOOLEAN");

//get the default value for property       
$object = new Object();
$defaultValue = $menuItem->getProperty() ? "true":"false";

$this->addSql("UPDATE tablename SET property = {$defaultValue}");

//not you can add constraint
$this->addSql("ALTER TABLE tablename ALTER property SET NOT NULL");

Avec cette réponse, je vous encourage à penser pourquoi avez-vous besoin de la valeur par défaut dans la base de données en premier lieu? Et généralement, cela permet de créer des objets avec une contrainte non nulle.

Dziamid
la source
3

Si vous utilisez la définition yaml pour votre entité, ce qui suit fonctionne pour moi sur une base de données postgresql:

Entity\Entity_name:
    type: entity
    table: table_name
    fields: 
        field_name:
            type: boolean
            nullable: false
            options:
                default: false
wonzbak
la source
1
Et si vous ne l'utilisiez pas $entity->setFieldName()avant de rincer? La doctrine semble définir la valeur par défaut à null. La seule solution dans yaml est de définir la valeur par défaut IN dans la classe d'entité qui me semble idiote puisqu'elle est déjà définie dans le yaml ... -_-
j0k
1

J'ai lutté avec le même problème. Je voulais avoir la valeur par défaut de la base de données dans les entités (automatiquement). Devinez quoi, je l'ai fait :)

<?php
/**
 * Created by JetBrains PhpStorm.
 * User: Steffen
 * Date: 27-6-13
 * Time: 15:36
 * To change this template use File | Settings | File Templates.
 */

require_once 'bootstrap.php';

$em->getConfiguration()->setMetadataDriverImpl(
    new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
        $em->getConnection()->getSchemaManager()
    )
);

$driver = new \Doctrine\ORM\Mapping\Driver\DatabaseDriver($em->getConnection()->getSchemaManager());
$driver->setNamespace('Models\\');

$em->getConfiguration()->setMetadataDriverImpl($driver);

$cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory();
$cmf->setEntityManager($em);
$metadata = $cmf->getAllMetadata();

// Little hack to have default values for your entities...
foreach ($metadata as $k => $t)
{
    foreach ($t->getFieldNames() as $fieldName)
    {
        $correctFieldName = \Doctrine\Common\Util\Inflector::tableize($fieldName);

        $columns = $tan = $em->getConnection()->getSchemaManager()->listTableColumns($t->getTableName());
        foreach ($columns as $column)
        {
            if ($column->getName() == $correctFieldName)
            {
                // We skip DateTime, because this needs to be a DateTime object.
                if ($column->getType() != 'DateTime')
                {
                    $metadata[$k]->fieldMappings[$fieldName]['default'] = $column->getDefault();
                }
                break;
            }
        }
    }
}

// GENERATE PHP ENTITIES!
$entityGenerator = new \Doctrine\ORM\Tools\EntityGenerator();
$entityGenerator->setGenerateAnnotations(true);
$entityGenerator->setGenerateStubMethods(true);
$entityGenerator->setRegenerateEntityIfExists(true);
$entityGenerator->setUpdateEntityIfExists(false);
$entityGenerator->generate($metadata, __DIR__);

echo "Entities created";
Steffen Brem
la source
3
Pour y revenir depuis plus de quelques années, je vous recommande de ne pas utiliser cette approche, c'est vraiment un hack hacky.
Steffen Brem
Puisque vous ne recommandez pas votre propre réponse, vous pourriez tout aussi bien la supprimer;)
Dragos
1

Bien que la définition de la valeur dans le constructeur fonctionne, l'utilisation des événements Doctrine Lifecycle pourrait être une meilleure solution.

En tirant parti de l' prePersistévénement Cycle de vie, vous pouvez définir votre valeur par défaut sur votre entité uniquement lors de la persistance initiale.

tiruncula
la source
L'utilisation d'événements de cycle de vie est considérée comme a hack. Ne comptez jamais sur les hacks.
emix
0

Soyez prudent lorsque vous définissez des valeurs par défaut sur la définition de propriété! Faites-le dans le constructeur à la place, pour le garder sans problème. Si vous le définissez lors de la définition de la propriété, puis conservez l'objet dans la base de données, puis effectuez un chargement partiel, les propriétés non chargées auront à nouveau la valeur par défaut. C'est dangereux si vous voulez à nouveau persister l'objet.

tomazahlin
la source