Standardwert in Doctrine


338

Wie setze ich in Lehre 2 einen Standardwert?


26
@ORM \ Column (Name = "foo", Typ = "dezimal", Genauigkeit = 7, Skalierung = 2, Optionen = {"Standard" = 0}) funktioniert (aus der nicht populären Antwort unten)
WayFarer

2
@ORM \ Column (name = "is_activated", type = "boolean", options = {"default": 0}) ODER @ORM \ Column (name = "is_activated", type = "boolean", options = {"default "= 0})
Ahmed Hamdy

Ahmed, dies scheint für Boolesche Werte in Symfony 2.3 nicht zu funktionieren. Options = {"default" = "0"}) funktioniert jedoch und setzt die Ganzzahl in Anführungszeichen.
Acyra

4
Wenn es ein Boolescher Wert ist, warum verwenden Sie nicht: options = {"default": false}?
Robocoder

Antworten:


385

Datenbankstandardwerte werden nicht "portabel" unterstützt. Die Standardmethode für Datenbankwerte kann nur über das columnDefinitionZuordnungsattribut verwendet werden, in dem Sie das SQLSnippet ( DEFAULTeinschließlich Ursache) für die Spalte angeben, der das Feld zugeordnet ist.

Sie können verwenden:

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

Standardwerte auf PHP-Ebene werden bevorzugt, da diese auch für neu erstellte und persistierte Objekte ordnungsgemäß verfügbar sind (Doctrine kehrt nach dem Persistieren eines neuen Objekts nicht zur Datenbank zurück, um die Standardwerte zu erhalten).


11
Hier gibt es jedoch ein Problem: Was ist, wenn ich einen "datetime" -Typ einstelle?
Artragis

46
@ Artragis setzen Sie Ihre Instanziierung in den Entitätskonstruktor
Alain Tiemblo

16
Bei Migrationen mit diesem Ansatz ist Vorsicht geboten, da vorhandene Zeilen dazu führen, dass die Migration fehlschlägt.
Tamlyn

7
Verwenden Sie den Instanziierungsbereich nicht zum Festlegen von Variablen ... Vertrauen Sie mir, es wird etwas Schlimmes passieren. Verwenden Sie stattdessen den Konstruktorbereich.
Mimoralea

4
Ich empfehle, die columnDefinition in der Annotation zu verwenden, oder jemand verwendet den MySQL-Client oder phpmyadmin und die Werte sind falsch ...
NDM

542
<?php
/**
 * @Entity
 */
class myEntity {
    /**
     * @var string
     *
     * @ORM\Column(name="myColumn", type="integer", options={"default" : 0})
     */
    private $myColumn;
    ...
}

Beachten Sie, dass hierfür SQL verwendet wird DEFAULT, das für einige Felder wie BLOBund nicht unterstützt wird TEXT.


4
Guter Fang! Es scheint, dass es in der offiziellen Dokumentation keine Option = {"default" = 0} gibt
WayFarer

2
Zu Ihrer Information, der optionsParameter ist auch für unsignedWerte nützlich . siehe diese Antwort
yvoyer

5
Ich benutze sowohl diese als auch die akzeptierte Antwort, um alle Grundlagen abzudecken. Auch nur ein Hinweis, den Sie auch tun können: options={"default": 0}Achten Sie darauf, "und nicht" zu verwenden, da dies Fehler in meiner Version der Doktrin verursacht.
Scott Flack

28
Dies sollte die ausgewählte Antwort sein!
Acelasi Eu

2
@Matt sagte er wahrscheinlich, dass undokumentierte Funktionen leicht entfernt werden können, da es sich um eine undokumentierte Funktion handelt. Wie es jetzt dokumentiert ist, sollten Sie sicher damit umgehen können.
JCM

62

Richten Sie einen Konstruktor in Ihrer Entität ein und legen Sie dort den Standardwert fest.


Dies scheint sicherlich der logische Ansatz zu sein. Hat jemand Probleme beim Einrichten von Standardeinstellungen im Konstruktor?
Cantera


@ cantera25 das sollte die Antwort sein +1
Phill Pafford

3
Dadurch werden vorhandene Entitäten nicht aktualisiert, wenn Sie ein neues Feld hinzufügen, das einen Standardwert haben muss. Also nein, das sollte nicht die Antwort sein. hängt davon ab, was genau Sie tun müssen
Tomáš Tibenský

Es funktioniert auch nicht zu Updatezwecken. Wenn Sie zum Standardwert zurückkehren möchten, indem Sie nur das Feld leeren (auch für Ganzzahlen), funktioniert dies leider nicht.
ThEBiShOp


51

Aktualisieren

Ein weiterer Grund, warum das Lesen der Dokumentation zu Symfony niemals aus dem Trend geraten wird. Für meinen speziellen Fall gibt es eine einfache Lösung, bei der die field typeOption empty_dataauf einen Standardwert gesetzt wird.

Auch diese Lösung gilt nur für das Szenario, in dem eine leere Eingabe in einem Formular das DB-Feld auf null setzt.

Hintergrund

Keine der vorherigen Antworten hat mir bei meinem speziellen Szenario geholfen, aber ich habe eine Lösung gefunden.

Ich hatte ein Formularfeld, das sich wie folgt verhalten musste:

  1. Nicht erforderlich, kann leer gelassen werden. (Verwendet 'erforderlich' => falsch)
  2. Wenn Sie dieses Feld leer lassen, sollte standardmäßig ein bestimmter Wert verwendet werden. Für eine bessere Benutzererfahrung habe ich nicht den Standardwert für das Eingabefeld festgelegt, sondern das HTML-Attribut 'Platzhalter' verwendet, da es weniger aufdringlich ist.

Ich habe dann alle hier gegebenen Empfehlungen ausprobiert. Lassen Sie mich sie auflisten:

  • Legen Sie einen Standardwert für die Entitätseigenschaft fest:
<?php
/**
 * @Entity
 */
class myEntity {
    /**
     * @var string
     *
     * @Column(name="myColumn", type="string", length="50")
     */
    private $myColumn = 'myDefaultValue';
    ...
}
  • Verwenden Sie die Optionsanmerkung:
@ORM\Column(name="foo", options={"default":"foo bar"})
  • Legen Sie den Standardwert für den Konstruktor fest:
/**
 * @Entity
 */
class myEntity {
    ...
    public function __construct()
    {
        $this->myColumn = 'myDefaultValue';
    }
    ...
}
Nichts davon hat funktioniert und alles aufgrund der Verwendung Ihrer Entity-Klasse durch Symfony.

WICHTIG

Symfony-Formularfelder überschreiben die in der Entitätsklasse festgelegten Standardwerte. Das bedeutet, dass in Ihrem Schema für Ihre Datenbank ein Standardwert definiert sein kann. Wenn Sie jedoch beim Senden Ihres Formulars ein nicht erforderliches Feld leer lassen, überschreibt die Methode form->handleRequest()in Ihrer form->isValid()Methode diese Standardwerte in Ihrer EntityKlasse und setzt sie auf die Eingabefeldwerte. Wenn die Eingabefeldwerte leer sind, wird die EntityEigenschaft auf festgelegt null.

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

Meine Problemumgehung

Stellen Sie den Standardwert auf Ihrem Controller nach form->handleRequest()Ihrer form->isValid()Methode ein:

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

Keine schöne Lösung, aber es funktioniert. Ich könnte wahrscheinlich ein machen , validation groupaber es kann sein , Menschen , die dieses Problem als siehe Datentransformation statt Datenvalidierung , ich überlasse es Ihnen zu entscheiden.


Setter überschreiben (funktioniert nicht)

Ich habe auch versucht, den EntitySetter folgendermaßen zu überschreiben :

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

    return $this;
}
...

Dies funktioniert nicht , obwohl es sauberer aussieht . Der Grund dafür ist, dass die böse form->handleRequest()Methode nicht die Setter-Methoden des Modells verwendet, um die Daten zu aktualisieren (siehe form->setData()für weitere Details).


Diese Antwort sollte auf jeden Fall nach oben gehen. Die Formularkomponente verwendet PropertyAccessor, um die Werte für Ihre Eigenschaften abzurufen und festzulegen. Vielleicht sollte der Property Accessor die Methoden verwenden, wenn sie verfügbar sind?
Xobb

1
Boolesche Spalten unterstützen keine Standardeinstellungen von PHP, also nur Anmerkungen
Crusader

Dies ist die einzige Lösung, die funktioniert hat, wenn Informationen aus Formularen stammen. Ich bin auch nicht einverstanden mit den obigen Kommentaren zu Booleschen Werten. Sie akzeptieren die Standardanmerkung nicht.
BernardA

Die Symfony-Formularkomponente verwendet Modellsetzer, jedoch nur, wenn sich die Modellformatdaten des Formulars von den Daten unterscheiden, die vom entsprechenden Getter der Modellobjektinstanz zurückgegeben werden. Wenn Sie über benutzerdefinierte Setter / Getter-Methoden verfügen, verwenden Sie die Formularoption "property_path" (wird von PropertyAccessor verarbeitet) oder den benutzerdefinierten DataMapper (ermöglicht das manuelle Definieren der Datenübertragungsroutine zwischen Formular und Modellobjekt).
Arkemlar

1
Bei dieser Frage geht es um Doktrin, nicht um Symfonie, daher ist diese Antwort nicht wirklich thematisch.
Omn

18

Die Problemumgehung, die ich verwendet habe, war a LifeCycleCallback. Ich warte immer noch darauf, ob es zum Beispiel mehr "native" Methoden gibt @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');
}

1
Verlassen Sie sich für zukünftige Leser nicht auf Rückrufe im Lebenszyklus :) Auch Marco Pivetta ist dagegen.
Emix

Warnung! Wenn die Entität die Eigenschaft dtPosted bereits festgelegt hat, überschreibt Ihr Code die Eigenschaft einfach. Verwenden Sie immer Accessoren, falls vorhanden! if (!$this->getDtPosted()) { $this->setDtPosted(new \DateTime()); }
Barh

13

Sie können dies auch mit XML tun:

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

8

So habe ich es für mich gelöst. Unten finden Sie ein Entitätsbeispiel mit dem Standardwert für MySQL. Dies erfordert jedoch auch die Einrichtung eines Konstruktors in Ihrer Entität und das Festlegen des Standardwerts dort.

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'

Mit dieser Zeile in meiner Konfiguration versucht Doctrine, die Standardeinstellung bei jeder Ausführung in der Spalte zu löschen. PHP App / Konsole Doktrin: Schema: Update
Shapeshifter

1
Dies ist die schlechteste Antwort hier. columnDefinitiongeht direkt gegen den Zweck eines ORM, der Abstraktion von der Datenbank ist. Diese Lösung beeinträchtigt die Portabilität, hält Ihre Software von Ihrem DB-Anbieter abhängig und beeinträchtigt auch die Tools von Doctrine Migrations.
Pedro Cordeiro

@ PedroCordeiro Ich stimme dir vollkommen zu. Dies ist nur eine schnelle Lösung, bis ein anderes Problem auftritt.
Putna

7

Funktioniert für mich auch in einer MySQL-Datenbank:

Entity\Entity_name:
    type: entity
    table: table_name
    fields: 
        field_name:
            type: integer
            nullable: true
            options:
                default: 1

Im Anmerkungsformat für wen interessiert: @ORM \ Column (name = "Entity_name", type = "integer", options = {"default" = "1"})
Hannes

7

Nichts davon hat bei mir funktioniert. Ich habe auf der Website der Doktrin eine Dokumentation gefunden, die besagt, dass der Wert direkt festgelegt werden soll, um einen Standardwert festzulegen.

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;

Dies fügte den Wert ein, den ich wollte.


Bitte ändern Sie den Link zu doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/… Siehe Punkt 3.2.2. Wie kann ich einer Spalte Standardwerte hinzufügen?
Tobi


3

Hinzufügen zu @romanb brillante Antwort.

Dies erhöht den Aufwand bei der Migration geringfügig, da Sie offensichtlich kein Feld ohne Null-Einschränkung und ohne Standardwert erstellen können.

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

Mit dieser Antwort ermutige ich Sie zu überlegen, warum Sie überhaupt den Standardwert in der Datenbank benötigen. Und normalerweise ist es möglich, das Erstellen von Objekten ohne Null-Einschränkung zu ermöglichen.


3

Wenn Sie die Yaml-Definition für Ihre Entität verwenden, funktioniert Folgendes für mich in einer Postgresql-Datenbank:

Entity\Entity_name:
    type: entity
    table: table_name
    fields: 
        field_name:
            type: boolean
            nullable: false
            options:
                default: false

1
Was ist, wenn Sie es $entity->setFieldName()vor dem Spülen nicht verwendet haben? Doctrine scheint den Standardwert bei null zu definieren. Die einzige Lösung in yaml besteht darin, den Standardwert IN der Entitätsklasse zu definieren, der mir dumm erscheint, da er bereits in yaml definiert ist ... -_-
j0k

1

Ich hatte mit dem gleichen Problem zu kämpfen. Ich wollte den Standardwert aus der Datenbank in die Entitäten (automatisch) haben. Ratet mal, ich habe es geschafft :)

<?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";

3
Um auf einige Jahre zurückzukommen, empfehle ich Ihnen, diesen Ansatz nicht zu verwenden. Es ist wirklich ein hackiger Hack.
Steffen Brem

Da Sie Ihre eigene Antwort nicht empfehlen, können Sie sie genauso gut löschen;)
Dragos

1

Während das Festlegen des Werts im Konstruktor funktionieren würde, könnte die Verwendung der Doctrine Lifecycle-Ereignisse eine bessere Lösung sein.

Durch die Nutzung des prePersistLifecycle-Ereignisses können Sie Ihren Standardwert für Ihre Entität nur beim anfänglichen Fortbestehen festlegen.


Die Verwendung von Lebenszyklusereignissen wird als a betrachtet hack. Verlassen Sie sich niemals auf Hacks.
Emix

0

Seien Sie vorsichtig, wenn Sie Standardwerte für die Eigenschaftsdefinition festlegen! Tun Sie es stattdessen im Konstruktor, um es problemlos zu halten. Wenn Sie es in der Eigenschaftsdefinition definieren, das Objekt in der Datenbank beibehalten und dann teilweise laden, haben nicht geladene Eigenschaften wieder den Standardwert. Das ist gefährlich, wenn Sie das Objekt erneut beibehalten möchten.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.