Valor predeterminado en Doctrine

338

¿Cómo configuro un valor predeterminado en Doctrine 2?

Jiew Meng
fuente
26
@ORM \ Column (name = "foo", type = "decimal", precision = 7, scale = 2, options = {"default" = 0}) funciona (de la respuesta no popular a continuación)
WayFarer
2
@ORM \ Column (name = "is_activated", type = "boolean", options = {"default": 0}) OR @ORM \ Column (name = "is_activated", type = "boolean", options = {"default "= 0})
ahmed hamdy
Ahmed, esto no parece funcionar para booleanos en Symfony 2.3. Sin embargo, options = {"default" = "0"}) funciona, colocando el entero entre comillas.
Acyra
44
Si es un booleano, ¿por qué no estás usando: options = {"default": false}?
robocoder

Respuestas:

385

Los valores predeterminados de la base de datos no son compatibles con "portabilidad". La única forma de usar los valores predeterminados de la base de datos es a través del columnDefinitionatributo de mapeo donde especifica el SQLfragmento (DEFAULT causa inclusiva) para la columna a la que se asigna el campo.

Puedes usar:

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

Se prefieren los valores predeterminados de nivel PHP, ya que también están disponibles correctamente en los objetos recién creados y persistentes (Doctrine no volverá a la base de datos después de persistir en un nuevo objeto para obtener los valores predeterminados).

romanb
fuente
11
pero hay un problema aquí: ¿Qué sucede si configuro un tipo "datetime"?
artragis
46
@artragis puso su instancia en el constructor de la entidad
Alain Tiemblo
16
Se debe tener cuidado con las migraciones utilizando este enfoque ya que cualquier fila existente hará que la migración falle.
Tamlyn
77
No utilice el área de creación de instancias para establecer variables ... Confía en mí, sucederá algo malo. Utilice el área del constructor en su lugar.
mimoralea
44
Recomiendo usar la definición de columna en la anotación, o alguien usará el cliente mysql o phpmyadmin y los valores estarán equivocados ...
NDM
542
<?php
/**
 * @Entity
 */
class myEntity {
    /**
     * @var string
     *
     * @ORM\Column(name="myColumn", type="integer", options={"default" : 0})
     */
    private $myColumn;
    ...
}

Tenga en cuenta que esto usa SQL DEFAULT, que no es compatible con algunos campos como BLOBy TEXT.

iv3ndy
fuente
44
¡Buena atrapada! Parece que no hay opción options = {"default" = 0} en la documentación oficial
WayFarer
2
Para su información, el optionsparámetro también es útil para los unsignedvalores. ver esta respuesta
yvoyer
55
Utilizo esto y la respuesta aceptada para cubrir todas las bases. También solo una nota que también puede hacer: options={"default": 0}tenga cuidado de usar "y no", ya que causa errores en mi versión de la doctrina.
Scott Flack
28
¡Esta debería ser la respuesta seleccionada!
Acelasi Eu
2
@Matt probablemente dijo eso porque era una característica no documentada, y las características no documentadas son propensas a ser eliminadas. Como está documentado ahora, debe estar seguro de usarlo.
JCM
62

Configure un constructor en su entidad y establezca el valor predeterminado allí.

Jeremy Hicks
fuente
Esto ciertamente parece ser el enfoque lógico. ¿Alguien ha tenido problemas con la configuración predeterminada en el constructor?
cantera
26
La solución recomendada de Doctrine: doctrine-project.org/docs/orm/2.1/en/reference/faq.html
cantera
@ cantera25 esa debería ser la respuesta +1
Phill Pafford
3
esto no actualiza las entidades existentes si agrega un nuevo campo que debe tener un valor predeterminado. así que no, esa no debería ser la respuesta. depende de qué es exactamente lo que necesita hacer
Tomáš Tibenský
Tampoco funcionará con fines de actualización. Si desea volver al valor predeterminado simplemente vaciando el campo (incluso para el entero), desafortunadamente, no funcionará.
ThEBiShOp
55

Utilizar:

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

y no:

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

Por ejemplo:

/**
* @ORM\Column(name="foo", type="smallint", options={"default":0})
*/
private $foo
Stanislav Terletskyi
fuente
2
Lo siento, tienes razón. Entonces puede encontrar una explicación en esta página: referencia de anotaciones de doctrina-orm
Stanislav Terletskyi
51

Actualizar

Una razón más por la que leer la documentación de Symfony nunca pasará de moda. Hay una solución simple para mi caso específico y es configurar la field typeopciónempty_data en un valor predeterminado.

Nuevamente, esta solución es solo para el escenario donde una entrada vacía en un formulario establece el campo DB en nulo.

Antecedentes

Ninguna de las respuestas anteriores me ayudó con mi escenario específico, pero encontré una solución.

Tenía un campo de formulario que debía comportarse de la siguiente manera:

  1. No es obligatorio, se puede dejar en blanco. (Usado 'requerido' => falso)
  2. Si se deja en blanco, debe tener un valor predeterminado. Para una mejor experiencia del usuario, no configuré el valor predeterminado en el campo de entrada, sino que usé el atributo html 'marcador de posición' ya que es menos molesto.

Luego probé todas las recomendaciones dadas aquí. Déjame enumerarlos:

  • Establezca un valor predeterminado cuando para la propiedad de entidad:
<?php
/**
 * @Entity
 */
class myEntity {
    /**
     * @var string
     *
     * @Column(name="myColumn", type="string", length="50")
     */
    private $myColumn = 'myDefaultValue';
    ...
}
  • Use la anotación de opciones:
@ORM\Column(name="foo", options={"default":"foo bar"})
  • Establezca el valor predeterminado en el constructor:
/**
 * @Entity
 */
class myEntity {
    ...
    public function __construct()
    {
        $this->myColumn = 'myDefaultValue';
    }
    ...
}
Nada de eso funcionó y todo por cómo Symfony usa tu clase de entidad.

IMPORTANTE

Los campos de formulario de Symfony anulan los valores predeterminados establecidos en la clase Entity. Es decir, su esquema para su base de datos puede tener un valor predeterminado definido, pero si deja un campo no requerido vacío al enviar su formulario, el form->handleRequest()interior de su form->isValid()método anulará esos valores predeterminados en su Entityclase y los establecerá en los valores del campo de entrada. Si los valores del campo de entrada están en blanco, establecerá la Entitypropiedad en null.

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

Mi solución

Establezca el valor predeterminado en su controlador después de form->handleRequest()dentro de su form->isValid()método:

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

No es una solución hermosa pero funciona. Probablemente podría hacer un validation grouppero puede haber personas que vean este problema como una transformación de datos en lugar de una validación de datos , lo dejo a usted para decidir.


Setter de anulación (no funciona)

También intenté anular el Entitysetter de esta manera:

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

    return $this;
}
...

Esto, a pesar de que se ve más limpio, no funciona . La razón es que el form->handleRequest()método malvado no usa los métodos de establecimiento del Modelo para actualizar los datos (profundice form->setData()para obtener más detalles).

Callistino
fuente
Esta respuesta debería ir a la cima con seguridad. El componente de formulario usa PropertyAccessor para obtener y establecer los valores de sus propiedades. ¿Quizás el descriptor de acceso a la propiedad debería usar los métodos cuando estén disponibles?
Xobb
1
las columnas booleanas no admiten valores predeterminados de php, por lo que solo las anotaciones
Crusader
Esta es la única solución que funcionó cuando la información proviene de los formularios. También estoy en desacuerdo con los comentarios anteriores sobre el booleano. No aceptan la anotación predeterminada.
BernardA
El componente de formulario de Symfony utiliza establecedores de modelo, pero solo cuando los datos de formato del modelo del formulario difieren con los datos devueltos por el captador correspondiente de la instancia del objeto modelo. Si tiene sus métodos personalizados setter / getter, use la opción de formulario "property_path" (será manejado por PropertyAccessor) o DataMapper personalizado (permite definir manualmente la rutina de transferencia de datos entre el formulario y el objeto modelo).
Arkemlar
1
Esta pregunta es sobre doctrina, no sobre Symfony, por lo que esta respuesta no es realmente sobre el tema.
Omn
18

La solución que usé fue a LifeCycleCallback. Todavía estoy esperando ver si hay algún otro método "nativo", por ejemplo @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
fuente
1
Para futuros lectores, no confíe en las devoluciones de llamada del ciclo de vida :) incluso Marco Pivetta está en contra de ellos.
emix
¡Advertencia! Si la entidad ya ha establecido la propiedad dtPosted, entonces su código simplemente sobrescribirá la propiedad. ¡Siempre use accesores si existen! if (!$this->getDtPosted()) { $this->setDtPosted(new \DateTime()); }
Barh
13

También puedes hacerlo usando 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
fuente
8

Así es como lo resolví por mí mismo. A continuación se muestra un ejemplo de entidad con valor predeterminado para MySQL. Sin embargo, esto también requiere la configuración de un constructor en su entidad, y para que establezca el valor predeterminado allí.

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
fuente
Con esta línea en mi configuración, Doctrine intenta eliminar el valor predeterminado en la columna cada vez que ejecuto. Aplicación php / doctrina de consola: esquema: actualización
cambiaformas el
1
Esta es la peor respuesta aquí. columnDefinitionva directamente contra el propósito de tener un ORM, que es la abstracción de la base de datos. Esta solución romperá la portabilidad, mantendrá su software dependiente de su proveedor de DB y también romperá las herramientas de Doctrine Migrations.
Pedro Cordeiro
@PedroCordeiro Estoy completamente de acuerdo contigo. Esta es solo una solución rápida hasta que surja otro problema.
Putna
7

Funciona para mí en una base de datos mysql también:

Entity\Entity_name:
    type: entity
    table: table_name
    fields: 
        field_name:
            type: integer
            nullable: true
            options:
                default: 1
Alex Hoang
fuente
En formato de anotación para quién está interesado: @ORM \ Column (name = "Entity_name", type = "integer", options = {"default" = "1"})
Hannes
7

Nada de esto funcionó para mí. Encontré documentación en el sitio de doctrina que dice establecer el valor directamente para establecer un valor predeterminado.

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;

Esto insertó el valor que quería.

métrico152
fuente
Cambie el enlace a doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/… Vea el punto 3.2.2. ¿Cómo puedo agregar valores predeterminados a una columna?
Tobi
3

Agregando a @romanb una respuesta brillante.

Esto agrega un poco de sobrecarga en la migración, porque obviamente no puede crear un campo sin restricción nula y sin valor predeterminado.

// 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");

Con esta respuesta, le animo a que piense, ¿por qué necesita el valor predeterminado en la base de datos en primer lugar? Y generalmente es para permitir crear objetos sin restricciones nulas.

Dziamid
fuente
3

Si usa la definición yaml para su entidad, lo siguiente funciona para mí en una base de datos postgresql:

Entity\Entity_name:
    type: entity
    table: table_name
    fields: 
        field_name:
            type: boolean
            nullable: false
            options:
                default: false
wonzbak
fuente
1
¿Qué pasa si no lo usó $entity->setFieldName()antes de enjuagar? Doctrine parece definir el valor predeterminado en nulo. La única solución en yaml es definir el valor predeterminado EN la clase de entidad que me parece tonto ya que ya está definido en el yaml ... -_-
j0k
1

Luché con el mismo problema. Quería tener el valor predeterminado de la base de datos en las entidades (automáticamente). Adivina qué, lo hice :)

<?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
fuente
3
Volviendo a esto desde hace unos años, le recomiendo que no use este enfoque, realmente es un truco hacky.
Steffen Brem
Dado que no recomienda su propia respuesta, es mejor que la elimine;)
Dragos
1

Si bien establecer el valor en el constructor funcionaría, usar los eventos de Doctrine Lifecycle podría ser una mejor solución.

Al aprovechar el prePersistEvento del ciclo de vida, puede establecer su valor predeterminado en su entidad solo en la persistencia inicial.

tiruncula
fuente
El uso de eventos de ciclo de vida se considera a hack. Nunca confíes en hacks.
emix
0

¡Tenga cuidado al establecer valores predeterminados en la definición de propiedad! Hazlo en el constructor, para mantenerlo libre de problemas. Si lo define en la definición de propiedad, luego persiste el objeto en la base de datos, luego realiza una carga parcial, luego las propiedades no cargadas nuevamente tendrán el valor predeterminado. Eso es peligroso si desea persistir el objeto nuevamente.

tomazahlin
fuente