Search code examples
phpormdoctrineforeign-keys

Doctrine 2: ORM, zero foreign key after flush


I'm using Doctrine 2 ORM and I have this problem.

I have three Entities, Address, Location and OrganizationalUnit.

Relations between them are: Address (OneToOne) Location, OrganizationalUnit (ManyToOne) Address.

Problem: When I'm trying to persist all objects (code below), it sets OrganizationalUnit(FK:Address) to zero instead of Address(ID).

When I'm selecting(find) or updating something it all works well.

Code:

Here is the code that makes the persists.

//Entity manager
$em = Zend_Registry::get('em');

$location = new Application_Model_Location();
$location->setLatitude(0);
$location->setLongitude(0);

$address = new Application_Model_Address();
$address->setCity('test');
$address->setCountry('cz');
$address->setNumber(123456);
$address->setPostalCode(123456);
$address->setStreet('street');

$address->setLocation($location);
$location->setAddress($address);

$organization = new Application_Model_Organizationalunit();
$organization->setName('novytest');
$organization->setType($organizationType[0]);

$address->addOrganization($organization);
$organization->setAddress($address);

$em->persist($location);
$em->persist($address);
$em->persist($organization);

$em->flush();

This code generates SQL like this (I have it from MySQL Server Log)

START TRANSACTION
INSERT INTO Location (longitude, latitude, cartographicSquare, id) VALUES ('0', '0', NULL, NULL)
INSERT INTO Address (street, number, city, postalCode, country, id) VALUES ('ulice', '123456', 'test', '123456', 'cz', 11)
INSERT INTO OrganizationalUnit (name, type, address, parentId) VALUES ('novytest', 7, 0, NULL)
ROLLBACK

Which is wrong because of "('novytest', 7, 0, NULL)" this zero (database model below)

Database look like this:

CREATE TABLE  `rotifera`.`location` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `longitude` double NOT NULL,
  `latitude` double NOT NULL,
  `cartographicSquare` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE  `rotifera`.`address` (
  `id` int(11) NOT NULL DEFAULT '0',
  `street` varchar(100) NOT NULL,
  `number` varchar(20) NOT NULL,
  `city` varchar(100) NOT NULL,
  `postalCode` varchar(10) NOT NULL,
  `country` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_Address_Location` (`id`),
  CONSTRAINT `fk_Address_Location` FOREIGN KEY (`id`) REFERENCES `location` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE  `rotifera`.`organizationalunit` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `type` int(11) NOT NULL,
  `address` int(11) DEFAULT NULL,
  `parentId` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fk_OrganizationalUnit_Address1` (`address`),
  KEY `fk_OrganizationalUnit_OrganizationalUnitType1` (`type`),
  KEY `fk_OrganizationalUnit_OrganizationalUnit1` (`parentId`),
  CONSTRAINT `fk_OrganizationalUnit_Address1` FOREIGN KEY (`address`) REFERENCES `address` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_OrganizationalUnit_OrganizationalUnit1` FOREIGN KEY (`parentId`) REFERENCES `organizationalunit` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `fk_OrganizationalUnit_OrganizationalUnitType1` FOREIGN KEY (`type`) REFERENCES `organizationalunittype` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

My ORM Models look like this

For Location:

/**
 * @Entity
 * @table(name="Location")
 */
class Application_Model_Location {

    /**
     * @id @column(type="integer")
     * @generatedValue
     */
    private $id;
    /** @column(type="float") */
    private $longitude;
    /** @column(type="float") */
    private $latitude;
    /** @column(type="integer", nullable=true) */
    private $cartographicSquare;
    /**
    * @OneToOne(targetEntity="Application_Model_Address", mappedBy="location")
    */
    private $address;

public function setId($id) {
    $this->id = $id;
}

public function getId() {
    return $this->id;
}

public function getLongitude() {
    return $this->longitude;
}

public function setLongitude($longitude) {
    $this->longitude = $longitude;
}

public function getLatitude() {
    return $this->latitude;
}

public function setLatitude($latitude) {
    $this->latitude = $latitude;
}

public function getCartographicSquare() {
    return $this->cartographicSquare;
}

public function setCartographicSquare($cartographicSquare) {
    $this->cartographicSquare = $cartographicSquare;
}

public function getAddress() {
    return $this->address;
}

public function setAddress($address) {
    $this->address = $address;
}

For Address:

/**
 * @Entity
 * @table(name="Address")
 */
class Application_Model_Address {

    /**
     * @id 
     * @column(type="integer")
     * @generatedValue
     */
    private $id;
    /** @column(length=100) */
    private $street;
    /** @column(length=100) */
    private $number;
    /** @column(length=100) */
    private $city;
    /** @column(length=100) */
    private $postalCode;
    /** @column(length=100) */
    private $country;
    /**
    * @OneToOne(targetEntity="Application_Model_Location", inversedBy="address")
    * @JoinColumn(name="id", referencedColumnName="id")
    */
    private $location;
    /**
    * @OneToMany(targetEntity="Application_Model_Organizationalunit", mappedBy="address")
    */
    private $organization;

public function setId($id) {
    $this->id = $id;
}

public function getId() {
    return ($this->id > 0) ? $this->id : $this->location->getId();
}

public function getStreet() {
    return $this->street;
}

public function setStreet($street) {
    $this->street = $street;
}

public function getNumber() {
    return $this->number;
}

public function setNumber($number) {
    $this->number = $number;
}

public function getCity() {
    return $this->city;
}

public function setCity($city) {
    $this->city = $city;
}

public function getPostalCode() {
    return $this->postalCode;
}

public function setPostalCode($postalCode) {
    $this->postalCode = $postalCode;
}

public function getCountry() {
    return $this->country;
}

public function setCountry($country) {
    $this->country = $country;
}

public function getLocation() {
    return $this->location;
}

public function setLocation($location) {
    $this->location = $location;
}

public function getOrganization() {
    return $this->organization;
}

public function addOrganization($organization) {
    $this->organization->add($organization);
}

public function __construct() {
    $this->organization = new \Doctrine\Common\Collections\ArrayCollection;
}

And for OrganizationalUnit

/**
 * @Entity
 * @table(name="OrganizationalUnit")
 */
class Application_Model_Organizationalunit {

    /**
     * @id 
     * @column(type="integer")
     * @generatedValue
     */
    private $id;
    /** @column(length=100) */
    private $name;
    /**
     * @ManyToOne(targetEntity="Application_Model_Organizationalunittype",
     *   inversedBy="id")
     * @JoinColumn(name="type",
     *   referencedColumnName="id")
     */
     private $type;
     /**
      * @ManyToOne(targetEntity="Application_Model_Address", inversedBy="organization")
      * @JoinColumn(name="address", referencedColumnName="id")
      */
    private $address;
    /**
     * @OneToOne(targetEntity="Application_Model_Organizationalunit")
     * @JoinColumn(name="parentId",
     *  referencedColumnName="id")
     */
    private $parentId;

public function setId($id) {
    $this->id = $id;
}

public function getId() {
    return $this->id;
}

public function getName() {
    return $this->name;
}

public function setName($name) {
    $this->name = $name;
}

public function getType() {
    return $this->type;
}

public function setType($type) {
    $this->type = $type;
}

public function getAddress() {
    return $this->address;
}

public function setAddress($address) {
    $this->address = $address;
}

public function getParentId() {
    return $this->parentId;
}

public function setParentId($parentId) {
    $this->parentId = $parentId;
}

I really don't know what's wrong with it, this problem not occur only here but also when I have ManyToMany relation and I'm inserting both sides in one flush.

Edit: And also I notice when I persist and flush only address and location, and vardump them after flush, only location have ID and address's is zero (before flush it's NULL).


Solution

  • This is not working because you have the relation between Address and Organisation declared twice.

    Change organisational to something like:

    /**
     * @Entity
     * @table(name="OrganizationalUnit")
     */
    class Application_Model_Organizationalunit {
    
        ...
    
        /**
         * @ManyToOne(targetEntity="Application_Model_Address", inversedBy="organisation")
         * @JoinColumn(name="address", referencedColumnName="id")
         */
        private $address;
    
        ...
    }
    
    /**
     * @Entity
     * @table(name="Address")
     */
    class Application_Model_Address {
    
        ...
    
        /**
         * @OneToMany(targetEntity="Application_Model_Organizationalunit", mappedBy="address")
         */
        private $organization;
    }
    

    EDIT Looked at your location model and found this line:

    /**
     * @OneToOne(targetEntity="Application_Model_Location", inversedBy="address")
     * @JoinColumn(name="id", referencedColumnName="id")
     */
    private $location;
    

    You reference your location to your primary key which autoincrements. Can it have something to do with this? You probably need to add a location_id column to your address table. Now you assign a value twice to your id column.

    Ps. it looks like there are more things not working correctly, so must try to solve the one at a time.