Search code examples
phpsymfonydoctrine-ormentity

Doctrine : Default value on joinColumn field


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)

Solution

  • 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