i have the following document:
class Purchase
{
/**
* #MongoDB\Id(strategy="INCREMENT")
*/
protected $id;
...
I'm using this document in a Symfony 2.8.4 project.
In this case, the ID for the first document that i persist is '1', the next one will be '2' and so on.
I'd like to start the counter from 1000, but i can't figure how i can do it inside the "Model part".
Thanks
Unfortunately there is now way to set counter in "Model part", but as the current counters are stored in the database you may alter their values there. For more details how this work you can inspect how IncrementGenerator::generate works.
It could be a perfect use of the SequenceGenerator, but not supported by the platform.
One work-around (out of the model, unfortunately) consists in setting the first ID manually.
In your controller:
$object = new Purchase();
$em = $this->getDoctrine()->getManager():
$metadata = $em->getClassMetadata(get_class($object));
$metadata->setIdGeneratorType($metadata::GENERATOR_TYPE_NONE);
$object->setId(1000);
$em->persist($object);
$em->flush();
print $object->getId(); // 1000
Don't forget to add the setter function in your Purchase document:
/**
* #param int $id
*
* #return Purchase
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
Then, remove this logic (or wrap it in a check to verify that the object is the first being persisted).
The inner drawback with senquenced identifier for a SQL table is the possiblity for a end user to easily go all over your tables. Sometimes it is a problem.
One solution is to create a non-sequenced id, something non guessable for every row.
This id must be a unique field, obviously. I can use a random function to generate thoses uniques ids for every row but there is a probability that it collides with a previously set id. If it collides, the end user will perceive it as random bug...
Here is one simple solution to overcome this problem:
$keyValid = false;
while(!$keyValid) {
// Create a new random key
$newKey = generateKey();
// Check if the key already exists in database
$existingPotato = $em->getRepository('Potato')->findOneBy(array('key' => $newKey));
if (empty($existingPotato)) {
$keyValid = true;
}
}
// Hooray, key is unique!
It forces me to make at least one SELECT statement everytime I want a new id.
So, is there a better, widely-accepted solution to this problem?
Alternatively, is there an optimised length for the id that make this problem irrelevant by making the collision probability negligable (for a 3,000,000 rows table)?
You can add a Custom id generation strategy to do it. You can implement it by creating a class that extends AbstractIdGenerator:
use Doctrine\ORM\Id\AbstractIdGenerator;
class NonSequencedIdGenerator extends AbstractIdGenerator
{
public function generate(\Doctrine\ORM\EntityManager $em, $entity)
{
$class = $em->getClassMetadata(get_class($entity));
$entityName = $class->getName();
do {
// You can use uniqid(), http://php.net/manual/en/function.uniqid.php
$id = generateKey();
} while($em->find($entityName, $id));
return $id;
}
}
Then add it using annotations in your entity class:
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="NonSequencedIdGenerator")
*/
private $id;
But if your generateKey don't return an unique identifier you should check if it already exists anyway. To avoid this, you can use an UUID generator for the primary keys in your entity as well.
/**
* #ORM\Id
* #ORM\Column(type="guid", unique=true)
* #ORM\GeneratedValue(strategy="UUID")
*/
private $id;
If you don't like this, you can create a new custom id generation that use UUID_SHORT, and use function like this to make it shorter.
use Doctrine\ORM\Id\AbstractIdGenerator;
class UuidShortGenerator extends AbstractIdGenerator
{
public function generate(EntityManager $em, $entity)
{
$conn = $em->getConnection();
return $conn->query('SELECT UUID_SHORT()')->fetchColumn(0);
}
}
The problem here is that I don't think it's provides full portability.
New to symfony2 and Doctrine.
How can I set a default value to the field foo_id (which is a reference on Foo table) to point on the ID 1 of the Foo table (which exists in all cases) ?
Me\NavigationBundle\Entity\PublicText:
type: entity
table: public_text
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
title:
type: string
length: '255'
nullable: false
content:
type: string
length: '2000'
nullable: false
manyToOne:
foo:
#How to set a default value???
targetEntity: \Me\NavigationBundle\Entity\Foo
joinColumn:
name: foo_id
referencedColumnName: id
nullable: false
lifecycleCallbacks: { }
I tried a lot of things without success :
Set default value to ID 1 in the constructor of Foo
Perform a request to retrieve the Foo object of ID 1 in the Me entity (could works, but bad practice)
Look up columnDefinition at Doctrine Column Annotations Reference¶
Pros: You can set up your own column definition
Cons: doctrine orm:schema-tool used by Doctrine (if you use it), gets confused and always reports columns that have a custom columnDefinition as changed. As it it will always tell you to run the change command for your column definition, as documented here:
SchemaTool will not detect changes on the column correctly anymore if you use "columnDefinition".
Example
/**
* #ManyToOne(targetEntity="YourType")
* #JoinColumn(
* name="your_type_id",
* referencedColumnName="id",
* nullable=false,
* columnDefinition="INT NOT NULL DEFAULT 1"
* )
*/
private $yourType;
Please note that using columnDefinition alone will work for generating migrations but will break the ORM context and potentially cause FK integrity issues. You will still need to add the object association to the ORM for persisting entities. See warnings from Ocramius
Example:
(new PublicText())
->getFoo(); //null - expected object(Foo)#1 (1) { ["id"] => int(1) }
I have seen many ways to achieve this in doctrine 2.
Constructor
In general, the quickest way and what most users do is require an association in the constructor.
public function __construct(Foo $foo)
{
$this->foo = $foo;
}
Then you can use getReference to retrieve it in your controller without needing to query the database. See http://doctrine-orm.readthedocs.org/en/latest/reference/advanced-configuration.html#reference-proxies
$foo = $em->getReference('app:Foo', 1);
$text = new \Path\To\Entity\PublicText($foo);
$em->persist($text);
$em->flush();
LifeCycleEvents
My preferred way Another method to set the default value with most ManyToOne relationships is to utilize the LifeCycleEvents http://doctrine-orm.readthedocs.org/en/latest/reference/events.html
Though it does have some caveats to be aware of. So be sure to RTM before implementing into production environments. In this case it should work fine, but I don't know your entire mapping structure.
use Doctrine\ORM\Event\LifecycleEventArgs;
/**
* #Entity
* #HasLifeCycleEvents
*/
class PublicText
{
// ...
/**
* #PrePersist
* #param \Doctrine\ORM\Event\LifecycleEventArgs $event
*/
public function onPrePersist(LifecycleEventArgs $event)
{
if (false === empty($this->foo)) {
return;
}
$this->foo = $event->getEntityManager()->getReference('app:Foo', 1);
}
}
Then in your controller.
$text = new \Path\To\Entity\PublicText;
$em->persist($text); //retrieve and set foo if not set in the Entity Event.
$em->flush();
Repository Method
Another option within your Entity is to just set the property's value using a Repository.
Define Repository Class in Entity
/**
* #Entity(repositoryClass="PublicTextRepo")
*/
class PublicText
{
// ...
}
Repository
use Doctrine\ORM\EntityRepository;
class PublicTextRepo extends EntityRepository
{
public function create()
{
$text = new \Path\To\Entity\PublicText;
$foo = $this->_em->getReference('app:Foo', 1);
$text->setFoo($foo );
return $text;
}
}
Then in your controller you can then do
$text = $em->getRepository('app:PublicText')->create();
$em->persist($text);
$em->flush();
Discriminator Map
Though not always viable depending on the use case. One of the ways I go about defining a default value of an Entity is creating a DiscriminatorMap with single table inheritance. http://doctrine-orm.readthedocs.org/en/latest/reference/inheritance-mapping.html#single-table-inheritance.
This way when the object is created the default value is automatically set in the database, and locks the object as that type. The issue is that the resulting value is not an object like it is in the other methods above.
To get the object's discriminator static value, you can use constants within the objects you define.
/**
* #Entity
* #InheritanceType("SINGLE_TABLE")
* #Table(name="user")
* #DiscriminatorColumn(name="type", type="string")
* #DiscriminatorMap({Person::TYPE="Person", Employee::TYPE="Employee"})
*/
class Person
{
const TYPE = 'person';
/**
* #return string [person|employee]
*/
public function getType()
{
return $this::TYPE;
}
// ...
}
/**
* #Entity
*/
class Employee extends Person
{
const TYPE = 'employee';
// ...
}
Then all you need to do in your controller is.
$employee = new \Path\To\Entity\Employee;
$em->persist($employee); //inserts with `user.type` as `employee`
$em->flush();
echo $employee->getType(); //employee
With annotation you can use : options={"default" = YourValue} on a #ORM\Column, So in yaml I think you can add
options:
default: yourValue
I'm not sure, i gie you an idea...
Based on this post: How to set the id of a foreign key id #sf2 #doctrine2
In the previous post I found this solution
class Item
{
/**
* #ORM\ManyToOne(targetEntity="MyBundle\Entity\ItemType", inversedBy="itemTypes")
* #ORM\JoinColumn(name="type_id", referencedColumnName="id")
*/
protected $item_type;
/**
*
* #var string $item_type_id
* #ORM\Column(type="integer")
*/
protected $item_type_id;
}
.... Setter & Getter
}
Which allows me to do something like that
$item = new Item();
$item->setItemTypeId(2); // Assuming that the ItemType with id 2 exists.
But from the last update of doctrine2.3 it's not working anymore.
when I persist the item(so creating the INSERT SQL query), it does not set the item_type_id field. only all other fields.
Any idea how to set manually the item_type_id without retrieve the ItemType just before setting it ? it's quite over use of queries !?
$item = new Item();
$itemType = $this->entity_manager->getRepository('Acme\MyBundle:ItemType')->find(2);
$item->setItemType($itemType); // Assuming that the ItemType with id 2 exists.
I've found the solution of this problem.
As we are working with an ORM, we usually do not use the identifier of an element and just work with the object itself.
But sometimes it is convenient to use the object ids instead of the objects, for example when we store an identifier in the session (ex: user_id, site_id, current_process_id,...).
In these circumstances, we should use Proxies, I'll refer to the Doctrine documentation for more information:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/advanced-configuration.html#reference-proxies
In this example we would then have something like this:
$itemTypeId = 2; // i.e. a valid identifier for ItemType
$itemType = $em->getReference('MyProject\Model\ItemType', $itemTypeId);
$item->setItemType($itemType);
Hope it will help others.
How do I set a default value in Doctrine 2?
<?php
/**
* #Entity
*/
class myEntity {
/**
* #var string
*
* #ORM\Column(name="myColumn", type="integer", options={"default" : 0})
*/
private $myColumn;
...
}
Note that this uses SQL DEFAULT, which is not supported for some fields like BLOB and TEXT.
Database default values are not "portably" supported. The only way to use database default values is through the columnDefinition mapping attribute where you specify the SQL snippet (DEFAULT cause inclusive) for the column the field is mapped to.
You can use:
<?php
/**
* #Entity
*/
class myEntity {
/**
* #var string
*
* #Column(name="myColumn", type="string", length="50")
*/
private $myColumn = 'myDefaultValue';
...
}
PHP-level default values are preferred as these are also properly available on newly created and persisted objects (Doctrine will not go back to the database after persisting a new object to get the default values).
Set up a constructor in your entity and set the default value there.
Use:
options={"default":"foo bar"}
and not:
options={"default"="foo bar"}
For instance:
/**
* #ORM\Column(name="foo", type="smallint", options={"default":0})
*/
private $foo
Update
One more reason why read the documentation for Symfony will never go out of trend. There is a simple solution for my specific case and is to set the field type option empty_data to a default value.
Again, this solution is only for the scenario where an empty input in a form sets the DB field to null.
Background
None of the previous answers helped me with my specific scenario but I found a solution.
I had a form field that needed to behave as follow:
Not required, could be left blank. (Used 'required' => false)
If left blank, it should default to a given value. For better user experience, I did not set the default value on the input field but rather used the html attribute 'placeholder' since it is less obtrusive.
I then tried all the recommendations given in here. Let me list them:
Set a default value when for the entity property:
<?php
/**
* #Entity
*/
class myEntity {
/**
* #var string
*
* #Column(name="myColumn", type="string", length="50")
*/
private $myColumn = 'myDefaultValue';
...
}
Use the options annotation:
#ORM\Column(name="foo", options={"default":"foo bar"})
Set the default value on the constructor:
/**
* #Entity
*/
class myEntity {
...
public function __construct()
{
$this->myColumn = 'myDefaultValue';
}
...
}
None of it worked and all because of how Symfony uses your Entity class.
IMPORTANT
Symfony form fields override default values set on the Entity class.
Meaning, your schema for your DB can have a default value defined but if you leave a non-required field empty when submitting your form, the form->handleRequest() inside your form->isValid() method will override those default values on your Entity class and set them to the input field values. If the input field values are blank, then it will set the Entity property to null.
http://symfony.com/doc/current/book/forms.html#handling-form-submissions
My Workaround
Set the default value on your controller after form->handleRequest() inside your form->isValid() method:
...
if ($myEntity->getMyColumn() === null) {
$myEntity->setMyColumn('myDefaultValue');
}
...
Not a beautiful solution but it works. I could probably make a validation group but there may be people that see this issue as a data transformation rather than data validation, I leave it to you to decide.
Override Setter (Does Not Work)
I also tried to override the Entity setter this way:
...
/**
* Set myColumn
*
* #param string $myColumn
*
* #return myEntity
*/
public function setMyColumn($myColumn)
{
$this->myColumn = ($myColumn === null || $myColumn === '') ? 'myDefaultValue' : $myColumn;
return $this;
}
...
This, even though it looks cleaner, it doesn't work. The reason being that the evil form->handleRequest() method does not use the Model's setter methods to update the data (dig into form->setData() for more details).
Here is how to do it in PHP 8 using attributes.
#[ORM\Column(type: 'boolean', nullable: false, options: ['default' => 0])]
#[Assert\NotNull()]
private bool $isFavorite = false;
The workaround I used was a LifeCycleCallback. Still waiting to see if there is any more "native" method, for instance #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');
}
You can do it using xml as well:
<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>
Here is how I solved it for myself. Below is an Entity example with default value for MySQL. However, this also requires the setup of a constructor in your entity, and for you to set the default value there.
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'
None of this worked for me. I found some documentation on doctrine's site that says to set the value directly to set a default value.
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;
This inserted the value I wanted.
Works for me on a mysql database also:
Entity\Entity_name:
type: entity
table: table_name
fields:
field_name:
type: integer
nullable: true
options:
default: 1
Adding to #romanb brilliant answer.
This adds a little overhead in migration, because you obviously cannot create a field with not null constraint and with no default value.
// 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");
With this answer, I encourage you to think why do you need the default value in the database in the first place? And usually it is to allow creating objects with not null constraint.
If you use yaml definition for your entity,
the following works for me on a postgresql database:
Entity\Entity_name:
type: entity
table: table_name
fields:
field_name:
type: boolean
nullable: false
options:
default: false
While setting the value in the constructor would work, using the Doctrine Lifecycle events might be a better solution.
By leveraging the prePersist Lifecycle Event, you could set your default value on your entity only on initial persist.
I struggled with the same problem. I wanted to have the default value from the database into the entities (automatically). Guess what, I did it :)
<?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";
Be careful when setting default values on property definition! Do it in constructor instead, to keep it problem-free. If you define it on property definition, then persist the object to the database, then make a partial load, then not loaded properties will again have the default value. That is dangerous if you want to persist the object again.