The composite foreign key points from RentalUnitAllocation to RentalUnit. 
Actually, the id attribute is already defining for a RentalUnit. But the 
point is, that I need to copy over the values of the lifetime column of the 
rental_unit table into the corresponding column of the 
rental_unit_allocation table. Perhaps it becomes more clear if one looks at 
the SQL definition:
CREATE TABLE rental_unit (
  id integer PRIMARY KEY,
  lifetime daterange DEFAULT daterange((now())::date, 'infinity'::date, '[)'
::text) NOT NULL
);

CREATE TABLE rental_unit_allocation (
  id integer PRIMARY KEY,
  rental_unit_id integer NOT NULL,
  rental_unit_lifetime daterange NOT NULL,
  period daterange DEFAULT daterange((now())::date, 'infinity'::date, '[)'::
text) NOT NULL,
  CONSTRAINT rental_unit_allocation_check CHECK ((period <@ 
rental_unit_period)),
  CONSTRAINT rental_unit_allocation_rental_unit_id_fkey
    FOREIGN KEY (rental_unit_id, rental_unit_period)
    REFERENCES rental_unit(id, period)
    MATCH FULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
);

I should explain the semantic behind. It rental unit has a lifetime. (Once, 
it is constructed and on some point of time it is teared down again.) Of 
course, a person should only be able to rent within this lifetime. Hence, 
there is a CHECK constraint that shall ensure that the rental period is a 
true sub-interval of the lifetime of the rental unit. Unfortunately, 
PostgreSQL only supports CHECK constraints within the same row. Hence, as a 
work-around the rental_unit.lifetime needs to be copied over as 
rental_unit_allocation.rental_unit_lifetime. (Of course, this unfortunately 
result into redudant information.) Now, to enforce that this redudant data 
is always in sync, the foreign key includes the lifetime, too.


Am Montag, 5. Januar 2015 17:34:45 UTC+1 schrieb kontakt:
>
> All the entities you pasted have only one @ORM\Id, which is the composite 
> one?
>
> On Mon, Jan 5, 2015 at 4:15 PM, <[email protected] <javascript:>> 
> wrote:
>
>> I use the latest Symfony framework with the included Doctrine bundle. 
>> Doctrine is at version 2.4.
>>
>> I have a entity with a composite foreign key that fails to persist, 
>> because the INSERT-statement only includes half of the foreign key.
>>
>> The scenario is simple: We have persons and rental units (aka apartments, 
>> rooms, etc.) that can be rented by persons. There is a many-to-many 
>> association between persons and rental units with an additional attribute 
>> "period" that defines the period of time that a person rents a rental unit. 
>> Hence, this many-to-many relation is broken up into two many-to-one 
>> relations and into a linking entity "rental unit allocation" that stores 
>> the period.
>>
>> Please note, that the type of period is a special PostgreSQL type 
>> ("daterange"). I wrote my own type class to handle this. The latter works 
>> fine, no problems here.
>>
>> So here is my simplified code (a left out all setters and getters as well 
>> as all unimportant attributes):
>> namespace HEK\HEKdbBundle\Entity;
>>
>> use Doctrine\ORM\Mapping as ORM;
>> use Doctrine\Common\Collections\ArrayCollection;
>> use HEK\HEKdbBundle\Type\DateRange;
>>
>> /**
>>  * @ORM\Entity
>>  * @ORM\Table( name = "person" )
>>  */
>> class Person extends Entity  {
>>
>>   public function __construct( $firstName, $lastName, \DateTime 
>> $birthday = null, Gender $gender = null, Country $country = null ) {
>>     $this->id = null;
>>     $this->rentalUnitAllocations = new ArrayCollection();
>>   }
>>
>>   /**
>>    * @ORM\Id
>>    * @ORM\Column( name = "id", type = "integer" )
>>    * @ORM\GeneratedValue( strategy = "SEQUENCE" )
>>    * @var int
>>    */
>>   protected $id = null;
>>
>>   /**
>>    * @ORM\OneToMany( targetEntity = "RentalUnitAllocation", mappedBy = 
>> "person" )
>>    * @var Doctrine\Common\Collections\ArrayCollection
>>    */
>>   protected $rentalUnitAllocations = null;
>> }
>>
>>
>> /**
>>  * @ORM\Entity
>>  * @ORM\Table( name = "rental_unit" )
>>  */
>> class RentalUnit extends Entity  {
>>
>>   public function __construct( DateRange $lifetime ) {
>>     $this->id = null;
>>     $this->lifetime = clone( $lifetime );
>>     $this->allocations = new ArrayCollection();
>>   }
>>
>>   /**
>>    * @ORM\Id
>>    * @ORM\Column( name = "id", type = "integer" )
>>    * @ORM\GeneratedValue( strategy = "SEQUENCE" )
>>    * @var int
>>    */
>>   protected $id = null;
>>
>>   /**
>>    * @ORM\Column( name = "lifetime", type = "daterange", nullable = false 
>> );
>>    * @var DateRange
>>    */
>>   protected $lifetime = null;
>>
>>   /**
>>    * @ORM\OneToMany( targetEntity = "RentalUnitAllocation", mappedBy = 
>> "rentalUnit" )
>>    * @var ArrayCollection
>>    */
>>   protected $allocations = null;
>> }
>>
>>
>> /**
>>  * @ORM\Entity()
>>  * @ORM\Table( name = "rental_unit_allocation" )
>>  */
>> class RentalUnitAllocation extends Entity  {
>>
>>   public function __construct( Person $person, RentalUnit $rentalUnit, 
>> DateRange $period ) {
>>     $this->id = null;
>>     $this->person = $person;
>>     $this->rentalUnit = $rentalUnit;
>>     $this->period = clone( $period );
>>   }
>>
>>   /**
>>    * @ORM\Id
>>    * @ORM\Column( name = "id", type = "integer" )
>>    * @ORM\GeneratedValue( strategy = "SEQUENCE" )
>>    * @var int
>>    */
>>   protected $id = null;
>>
>>   /**
>>    * @ORM\ManyToOne( targetEntity = "Person", inversedBy = 
>> "rentalUnitAllocations" )
>>    * @ORM\JoinColumn( name = "person_id", referencedColumnName = "id", 
>> nullable = false )
>>    * @var Person
>>    */
>>   protected $person = null;
>>
>>   /**
>>    * @ORM\ManyToOne( targetEntity = "RentalUnit", inversedBy = 
>> "allocations" )
>>    * @ORM\JoinColumns = {
>>    *      @ORM\JoinColumn( name = "rental_unit_id", referencedColumnName 
>> = "id", nullable = false ),
>>    *      @ORM\JoinColumn( name = "rental_unit_lifetime", 
>> referencedColumnName = "lifetime", nullable = false )
>>    * }
>>    * @var RentalUnit
>>    */
>>   protected $rentalUnit = null;
>>
>>   /**
>>    * @ORM\Column( name = "period", type = "daterange", nullable = false )
>>    * @var DateRange
>>    */
>>   protected $period = null;
>> }
>>
>> The code to persist a new rental unit allocation looks like
>> $em = $this->getDoctrine()->getManager();
>> $person = $em->getReference( 'HEKdbBundle:Person', 1 );
>> $rentalUnit = $em->getRepository('HEKdbBundle:RentalUnit')->find( 42 );
>> $period = new DateRange( '2015-01-01', null );
>> $alloc = new RentalUnitAllocation( $person, $rentalUnit, $period );
>> $em->persist( $alloc );
>> $em->flush();
>>
>> And the error message is
>>
>> NotNullConstraintViolationException: An exception occurred while 
>> executing 'INSERT INTO rental_unit_allocation (id, period, person_id, 
>> rental_unit_id) VALUES (?, ?, ?, ?)' with params [12, "[ 2015-01-01, 
>> infinity )", 1, 42]:
>>
>> SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column 
>> "rental_unit_lifetime" violates not-null constraint
>>
>> The error message from PostgreSQL is correct, because the INSERT 
>> statement actually lacks the column rental_unit_lifetime column. But why?
>>
>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "doctrine-user" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to [email protected] <javascript:>.
>> To post to this group, send email to [email protected] 
>> <javascript:>.
>> Visit this group at http://groups.google.com/group/doctrine-user.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"doctrine-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/doctrine-user.
For more options, visit https://groups.google.com/d/optout.

Reply via email to