Doctrine 2: Use default 0 values instead of null for relation

doctrine, doctrine-orm, mysql

Solution

No, this is not possible.

`NULL` has a very specific meaning in SQL. It represents "no value", and you can verify that your logic won't work even at SQL level:

CREATE TABLE foo (
    `id` INT(11) PRIMARY KEY AUTO_INCREMENT,
    `bar_id` INT(11)
);

CREATE TABLE `bar` (`id` INT(11) PRIMARY KEY AUTO_INCREMENT);

ALTER TABLE foo ADD FOREIGN KEY `bar_id_fk` (`bar_id`) REFERENCES `bar` (`id`);

INSERT INTO `bar` VALUES (NULL);
INSERT INTO `bar` VALUES (NULL);
INSERT INTO `bar` VALUES (NULL);
INSERT INTO `foo` VALUES (NULL, 1);
INSERT INTO `foo` VALUES (NULL, 2);
INSERT INTO `foo` VALUES (NULL, 3);
INSERT INTO `foo` VALUES (NULL, 0);

/*
    ERROR 1452 (23000): 
        Cannot add or update a child row: a foreign key constraint fails
        (`t2`.`foo`, CONSTRAINT `foo_ibfk_1` FOREIGN KEY (`bar_id`) 
        REFERENCES `bar` (`id`))
*/

INSERT INTO `foo` VALUES (NULL, 4);

/*
    ERROR 1452 (23000): 
        Cannot add or update a child row: a foreign key constraint fails
        (`t2`.`foo`, CONSTRAINT `foo_ibfk_1` FOREIGN KEY (`bar_id`) 
        REFERENCES `bar` (`id`))
*/

INSERT INTO `foo` VALUES (NULL, NULL); /* VALID! */

So no, you cannot have Doctrine ORM behave so that `0` is interpreted as `NULL`, since that's not allowed by the RDBMS itself.

What you can do is inserting "fake" referenced entries in your DB, which will then act as null object when hydrated as entities:

INSERT INTO `bar` VALUES (NULL);
UPDATE `bar` SET `id` = 0 WHERE `id` = 4;

INSERT INTO `foo` VALUES (NULL, 0); /* now works! */

In entity terms, it looks quite similar.

(Please note that `public` properties are ONLY supported from Doctrine ORM 2.4, which is not yet released. They make things easier to read here, though)

Foo.php:

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 * @ORM\Table(name="foo")
 */
class Foo
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    public $id;

    /**
     * @ORM\ManyToOne(targetEntity="Bar")
     * @ORM\JoinColumn(name="bar_id", referencedColumnName="id", nullable=false)
     */
    public $bar;
}

Bar.php:

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity()
 * @ORM\Table(name="bar")
 */
class Bar
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id()
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    public $id;
}

And then the code to produce a new `Foo` instance:

$nullBar  = $entityManager->find('Bar', 0);
$foo      = new Foo();
$foo->bar = $nullBar;

$em->persist($foo);
$em->flush();

Problem

Is it possible to use the value 0 instead of null for relations (many-to-one, one-to-one) with Doctrine 2? Now I've got a lot of NOT NULL columns witch I may not change to null values. Changing the default value in MySQL to 0 it self isn't the solution becease doctrine always sets the column for inserting/updating rows.

Original source