doctrine / orm

Doctrine Object Relational Mapper (ORM)
https://www.doctrine-project.org/projects/orm.html
MIT License
9.94k stars 2.52k forks source link

Composite foreign keys with shared columns #7859

Open scaytrase opened 5 years ago

scaytrase commented 5 years ago
Q A
Version 2.6.3

Support Question

We have the following structure (simplified)

brand:
    id: int auto increment, 
    primary key(id)

brand_series:
    brand_id -> brand.id not null
    name: string not null
    primary key (brand_id, name)

product:
    id: int auto increment,
    brand_id -> brand.id not null
    series_name: string nullable // (product could be outside the series but still belongs to brand)

    (brand_id, series_name) -> series (brand_id, name)
    primary key (id)

Having read model for this structure work OK, with ManyToOne bi-directional associations. But we have a problem when we try to execute product.setSeries(null)

At this moment doctrine tries to set both brand_id and series_name fields to null causing brand_id foreign key constraint to fail.

Is it possible to mark some of the JoinColumn mappings as read-only in order to skip them in changeset computation?

SenseException commented 5 years ago

@scaytrase Can you please create an example for these 3 entities? I would like to see the mapping and methods of these entities for reproducing this behavior.

scaytrase commented 5 years ago

Sure, a bit later

scaytrase commented 5 years ago

https://gist.github.com/scaytrase/7cc8c22b583be218a51f620e69b06ef8

here it is. the problem begins when you call $option->removeMember($member). At this moment you should call $member->setSelectedOption(null) in order to detach object foreign key relation, but nullifying this relation also tries to make brand_id=null (as part of foreign key) which breaks data structure (member should be bound to group, but group option selection is not mandatory).

This is simplified a bit, of course I should assert that i selecting the option from the same group member belongs to and initialize some arraycollections, but I think it's enough to demonstrate the problem

what I want is somehow tell the doctrine that in the relation join columns

     * @ORM\JoinColumns({
     *       @ORM\JoinColumn(name="group_id", referencedColumnName="id")),
     *       @ORM\JoinColumn(name="group_option_name", referencedColumnName="option_name"))
     * })

the @ORM\JoinColumn(name="group_id", referencedColumnName="id")) is not controlled by this relation (a.k.a readonly join column) and it should not be touched when the changesets are computed. Nullability of the relation should be determined by group_option_name column

drupol commented 3 years ago

Hello, sorry to dig up old topics, I'm trying to find a solution as well to the same kind of issue.

I've setup a demo repository where I can experiment. I opened a PR trying to find a solution to this.

Can you have a look and let me know if it is the same kind of issue that we are having?

smilesrg commented 4 months ago

@scaytrase I found that there is incorrect usage: $this->members->removeElement($member); should be used instead of $this->members->removeElement($member);, but I don't know if it caused the problem