Sorry, there were some copy-and-paste errors. This is the correct SQL:
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_lifetime)),
  CONSTRAINT rental_unit_allocation_rental_unit_id_fkey
    FOREIGN KEY (rental_unit_id, rental_unit_lifetime)
    REFERENCES rental_unit(id, lifetime)
    MATCH FULL ON UPDATE CASCADE DEFERRABLE INITIALLY DEFERRED;
);



Am Montag, 5. Januar 2015 18:06:45 UTC+1 schrieb [email protected]:
>
> 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]> 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].
>>> 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.
>>>
>>
>>

-- 
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