Wartość domyślna w Doctrine

338

Jak ustawić wartość domyślną w Doctrine 2?

Jiew Meng
źródło
26
@ORM \ Kolumna (nazwa = „foo”, typ = „dziesiętny”, precyzja = 7, skala = 2, opcje = {„domyślny” = 0}) działa (z
mało
2
@ORM \ Column (name = "is_activated", type = "boolean", options = {"default": 0}) LUB @ORM \ Column (name = "is_activated", type = "boolean", options = {"default „= 0})
Ahmed Hamdy
Ahmed, to nie wydaje się działać na booleany w Symfony 2.3. Jednak options = {"default" = "0"}) działa, wpisując liczbę całkowitą w cudzysłowie.
Acyra,
4
Jeśli jest to wartość logiczna, dlaczego nie używasz: options = {"default": false}?
robocoder,

Odpowiedzi:

385

Domyślne wartości bazy danych nie są obsługiwane „przenośnie”. Jedynym sposobem użycia domyślnych wartości bazy danych jest użycie columnDefinitionatrybutu mapowania, w którym określa się SQLfragment DEFAULTkodu ( łącznie z przyczyną) dla kolumny, na którą mapowane jest pole.

Możesz użyć:

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

Preferowane są wartości domyślne na poziomie PHP, ponieważ są one również odpowiednio dostępne dla nowo utworzonych i utrwalonych obiektów (Doctrine nie wróci do bazy danych po utrwaleniu nowego obiektu w celu uzyskania wartości domyślnych).

romanb
źródło
11
ale tutaj jest problem: co, jeśli ustawię typ „datetime”?
artragis,
46
@artragis włożył instynkt do konstruktora encji
Alaina Tiemblo
16
W przypadku migracji należy zachować ostrożność, ponieważ wszelkie istniejące wiersze spowodują niepowodzenie migracji.
Tamlyn
7
Nie używaj obszaru tworzenia instancji do ustawiania zmiennych ... Zaufaj mi, coś złego się stanie. Zamiast tego użyj obszaru konstruktora.
mimoralea
4
Polecam użyć w kolumnie adnotacji columnDefinition, albo ktoś użyje klienta mysql lub phpmyadmin, a wartości będą niepoprawne ...
NDM
542
<?php
/**
 * @Entity
 */
class myEntity {
    /**
     * @var string
     *
     * @ORM\Column(name="myColumn", type="integer", options={"default" : 0})
     */
    private $myColumn;
    ...
}

Zauważ, że używa to SQL DEFAULT, który nie jest obsługiwany w niektórych polach, takich jak BLOBi TEXT.

iv3ndy
źródło
4
Dobry chwyt! Wygląda na to, że w oficjalnej dokumentacji nie ma opcji = {"default" = 0}
WayFarer
2
FYI, optionsparametr jest również przydatny dla unsignedwartości. zobacz tę odpowiedź
yvoyer
5
Używam zarówno tej, jak i przyjętej odpowiedzi, aby objąć wszystkie bazy. Również uwaga, którą możesz zrobić: options={"default": 0}Uważaj, aby używać „i nie”, ponieważ powoduje to błędy w mojej wersji doktryny.
Scott Flack,
28
To powinna być wybrana odpowiedź!
Acelasi UE
2
@Matt prawdopodobnie powiedział, że ponieważ była to funkcja nieudokumentowana, a funkcje nieudokumentowane są podatne na usuwanie. Jak to teraz udokumentowano, powinieneś być bezpieczny w użyciu.
JCM
62

Skonfiguruj konstruktor w swojej encji i ustaw tam domyślną wartość.

Jeremy Hicks
źródło
Z pewnością wydaje się to logicznym podejściem. Czy ktoś napotkał problemy z konfigurowaniem ustawień domyślnych w konstruktorze?
cantera
26
Zalecane rozwiązanie Doctrine: doctrine-project.org/docs/orm/2.1/en/reference/faq.html
cantera
@ cantera25 powinna to być odpowiedź +1
Phill Pafford,
3
nie aktualizuje to istniejących jednostek, jeśli dodasz nowe pole, które musi mieć wartość domyślną. więc nie, to nie powinna być odpowiedź. zależy od tego, co dokładnie musisz zrobić
Tomáš Tibenský
To też nie będzie działać w celu aktualizacji. Jeśli chcesz wrócić do wartości domyślnej, po prostu opróżniając pole (nawet dla liczb całkowitych), niestety to nie zadziała.
ThEBiShOp,
55

Posługiwać się:

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

i nie:

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

Na przykład:

/**
* @ORM\Column(name="foo", type="smallint", options={"default":0})
*/
private $foo
Stanisław Terletskij
źródło
2
Przepraszam, masz rację. Możesz znaleźć wyjaśnienie na tej stronie: doktryna-orm adnotacje-odniesienie
Stanislav Terletskyi
51

Aktualizacja

Jeszcze jeden powód, dla którego przeczytaj dokumentację Symfony, która nigdy nie wyjdzie z mody. Istnieje proste rozwiązanie dla mojego konkretnego przypadku i polega na ustawieniu field typeopcji empty_datana wartość domyślną.

Ponownie, to rozwiązanie jest tylko dla scenariusza, w którym puste dane wejściowe w formularzu ustawiają pole DB na null.

tło

Żadna z poprzednich odpowiedzi nie pomogła mi w moim konkretnym scenariuszu, ale znalazłem rozwiązanie.

Miałem pole formularza, które musiało zachowywać się następująco:

  1. Nie wymagane, można pozostawić puste. (Używane „wymagane” => fałsz)
  2. Jeśli pole pozostanie puste, powinno przyjąć domyślną wartość. Dla lepszego doświadczenia użytkownika nie ustawiłem domyślnej wartości w polu wejściowym, ale raczej użyłem atrybutu HTML „symbol zastępczy”, ponieważ jest on mniej nachalny.

Następnie wypróbowałem wszystkie podane tutaj zalecenia. Pozwól mi je wymienić:

  • Ustaw wartość domyślną, gdy dla właściwości encji:
<?php
/**
 * @Entity
 */
class myEntity {
    /**
     * @var string
     *
     * @Column(name="myColumn", type="string", length="50")
     */
    private $myColumn = 'myDefaultValue';
    ...
}
  • Użyj adnotacji opcji:
@ORM\Column(name="foo", options={"default":"foo bar"})
  • Ustaw wartość domyślną dla konstruktora:
/**
 * @Entity
 */
class myEntity {
    ...
    public function __construct()
    {
        $this->myColumn = 'myDefaultValue';
    }
    ...
}
Nic z tego nie działało, a wszystko z powodu tego, jak Symfony używa twojej klasy Entity.

WAŻNY

Pola formularza Symfony zastępują wartości domyślne ustawione dla klasy Entity. Oznacza to, że w schemacie dla bazy danych może być zdefiniowana wartość domyślna, ale jeśli pozostawisz niepotrzebne pole puste podczas przesyłania formularza, form->handleRequest()wnętrze form->isValid()metody zastąpi te wartości domyślne w Entityklasie i ustawi je na wartości pola wejściowego. Jeśli wartości pola wejściowego są puste, Entitywłaściwość zostanie ustawiona na null.

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

Moje obejście

Ustawić wartość domyślną na kontrolerze po form->handleRequest()wewnątrz form->isValid()metody:

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

Nie jest to piękne rozwiązanie, ale działa. Prawdopodobnie mógłbym zrobić, validation groupale mogą być ludzie, którzy postrzegają ten problem jako transformację danych, a nie sprawdzanie poprawności danych , pozostawiam to tobie do podjęcia decyzji.


Zastąp seter (nie działa)

Próbowałem również zastąpić Entityseter w ten sposób:

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

    return $this;
}
...

To, chociaż wygląda na czystsze, nie działa . Powodem jest to, że form->handleRequest()metoda zła nie używa metod ustawiających Model do aktualizacji danych ( form->setData()po więcej szczegółów).

Callistino
źródło
Ta odpowiedź z pewnością powinna znaleźć się na górze. Składnik formularza używa PropertyAccessor, aby uzyskać i ustawić wartości dla swoich właściwości. Może podmiot przystępujący do nieruchomości powinien skorzystać z metod, gdy są one dostępne?
Xobb,
1
kolumny boolowskie nie obsługują wartości domyślnych z php, więc tylko adnotacje
Crusader
To jedyne rozwiązanie, które działało, gdy informacje pochodzą z formularzy. Nie zgadzam się również z powyższymi komentarzami dotyczącymi boolean. Nie akceptują domyślnej adnotacji.
Bernard,
Komponent formularza Symfony używa ustawiaczy modeli, ale tylko wtedy, gdy dane formatu formularza w formularzu różnią się od danych zwróconych przez odpowiedni moduł pobierania obiektu modelu Jeśli masz niestandardowe metody ustawiające / pobierające - użyj opcji formularza „ścieżka_właściwości” (będzie obsługiwane przez PropertyAccessor) lub niestandardowej DataMapper (pozwala ręcznie zdefiniować procedurę przesyłania danych między formularzem a obiektem modelu).
Arkemlar,
1
To pytanie dotyczy doktryny, a nie symfony, więc ta odpowiedź tak naprawdę nie dotyczy tematu.
Omn
18

Obejście Kiedyś było LifeCycleCallback. Na przykład wciąż czekam, aby sprawdzić, czy istnieje jakaś „natywna” metoda @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
źródło
1
Dla przyszłych czytelników nie polegaj na zwrotach cyklu życia :) nawet Marco Pivetta jest przeciwko nim.
emix
Ostrzeżenie! Jeśli jednostka już ustawiła właściwość dtPosted, wówczas kod po prostu nadpisze właściwość. Zawsze używaj akcesoriów, jeśli istnieją! if (!$this->getDtPosted()) { $this->setDtPosted(new \DateTime()); }
Barh
13

Możesz to zrobić również za pomocą 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
źródło
8

Oto jak sam to rozwiązałem. Poniżej znajduje się przykład jednostki z domyślną wartością dla MySQL. Wymaga to jednak również skonfigurowania konstruktora w jednostce i ustawienia wartości domyślnej.

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
źródło
Z tym wierszem w mojej konfiguracji Doctrine próbuje upuścić domyślną kolumnę przy każdym uruchomieniu. doktryna aplikacji / konsoli php: schemat: aktualizacja
zmiennokształtny
1
To jest najgorsza odpowiedź tutaj. columnDefinitionidzie bezpośrednio ponownie w celu posiadania ORM, czyli abstrakcji z bazy danych. To rozwiązanie zakłóci przenośność, sprawi, że twoje oprogramowanie będzie zależne od dostawcy DB, a także złamie narzędzia Doctrine Migrations.
Pedro Cordeiro,
@PedroCordeiro Całkowicie się z tobą zgadzam. Jest to tylko szybkie rozwiązanie, dopóki nie pojawi się kolejny problem.
Putna
7

Działa dla mnie również w bazie danych mysql:

Entity\Entity_name:
    type: entity
    table: table_name
    fields: 
        field_name:
            type: integer
            nullable: true
            options:
                default: 1
Alex Hoang
źródło
W formacie dla zainteresowanych: @ORM \ Column (name = "Entity_name", type = "integer", options = {"default" = "1"})
Hannes
7

Nic z tego nie działało dla mnie. Znalazłem trochę dokumentacji na stronie doktryny, która mówi, aby ustawić wartość bezpośrednio, aby ustawić wartość domyślną.

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;

To wstawiło wartość, którą chciałem.

metryczny152
źródło
Zmień link na doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/... Patrz punkt 3.2.2. Jak mogę dodać wartości domyślne do kolumny?
Tobi
3

Dodanie do świetnej odpowiedzi na @romanb.

Dodaje to trochę narzutu przy migracji, ponieważ oczywiście nie można utworzyć pola bez ograniczenia zerowego i bez wartości domyślnej.

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

Dzięki tej odpowiedzi zachęcam do zastanowienia się, dlaczego w ogóle potrzebujesz domyślnej wartości w bazie danych? I zwykle pozwala na tworzenie obiektów bez ograniczenia zerowego.

Dziamid
źródło
3

Jeśli używasz definicji yaml dla swojej encji, następujące działania działają na mnie w bazie danych postgresql:

Entity\Entity_name:
    type: entity
    table: table_name
    fields: 
        field_name:
            type: boolean
            nullable: false
            options:
                default: false
wonzbak
źródło
1
Co jeśli nie $entity->setFieldName()używałeś przed spłukaniem? Doctrine wydaje się definiować wartość domyślną na zero. Jedynym rozwiązaniem w YAML jest określenie wartości domyślnej W klasie podmiot, który wydaje mi się głupie, ponieważ już zdefiniowane w YAML ... -_-
J0K
1

Walczyłem z tym samym problemem. Chciałem mieć domyślną wartość z bazy danych do encji (automatycznie). Zgadnij co, zrobiłem to :)

<?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
źródło
3
Wracając do tego od kilku lat, zalecam, abyście nie stosowali tego podejścia, to naprawdę hacky hack.
Steffen Brem
Ponieważ nie polecasz własnej odpowiedzi, równie dobrze możesz ją usunąć;)
Dragos
1

Chociaż ustawienie wartości w konstruktorze działałoby, lepszym rozwiązaniem może być użycie zdarzeń cyklu życia Doctrine.

Wykorzystując prePersistZdarzenie cyklu życia, możesz ustawić wartość domyślną dla swojej jednostki tylko przy początkowym utrwaleniu.

tiruncula
źródło
Używanie zdarzeń cyklu życia uważa się za hack. Nigdy nie polegaj na hakach.
emix
0

Zachowaj ostrożność podczas ustawiania wartości domyślnych w definicji właściwości! Zrób to w konstruktorze, aby zachować bezproblemowe działanie. Jeśli zdefiniujesz to w definicji właściwości, utrwal obiekt w bazie danych, a następnie wykonaj częściowe ładowanie, a następnie właściwości nie załadowane ponownie będą miały wartość domyślną. Jest to niebezpieczne, jeśli chcesz ponownie utrwalić obiekt.

tomazahlin
źródło