Does doctrine support composite keys in many-to-many join tables?

Viewed 722

I have entities Channel and Post. Channel has simple integer id. Post has composite key made of its own id and channel_id.

I would like to have many-to-many relationship between those entities(mention), so that one Post might have many mentioned Channels and Channel might be mentioned by many Posts.

The question is - if it is supported by Doctrine, how do I implement such relationship?

Domain\Telegram\Models\Channel:
  type: entity
  id:
    id:
      type: integer

  fields:
    name:
      type: string
      nullable: true

  oneToMany:
    posts:
      targetEntity: Domain\Telegram\Models\Post
      mappedBy: channel


Domain\Telegram\Models\Post:
  type: entity
  id:
    channel:
      associationKey: true
    id:
      type: integer

  manyToOne:
    channel:
      targetEntity: Domain\Telegram\Models\Channel
      inversedBy: posts

Update and solution

Solution by @WilliamPerron almost worked. If used in its current state, doctrine:schema:validate returns an error :

[Mapping] FAIL - The entity-class 'Domain\Telegram\Models\Channel' mapping is invalid:

The inverse join columns of the many-to-many table 'mentions' have to contain to ALL identifier columns of the target entity 'Domain\Telegram\Models\Post', however 'channel_id' are missing.

inverseJoinColumns section should look like this:

    inverseJoinColumns:
      post_id:
        referencedColumnName: id
      post_channel_id:
        referencedColumnName: channel_id

So this kind of relationship is practically same as simple many-to-many relationship.

1 Answers

Yes, this is possible

You simply need to specifify which table it joins to with the groups element, and then the entity it maps to. For a uniderectional relationship, the schema would look like this:

Channel:
  type: entity
  manyToMany:
    posts:
      targetEntity: Post
      joinTable:
        name: posts_channels
        joinColumns:
          channel_id:
            referencedColumnName: id
        inverseJoinColumns:
          post:
            referencedColumnName: id

and for a bidirectional relationship, you would need to add the inversedBy element:

Channel:
  type: entity
  manyToMany:
    posts:
      targetEntity: Post
      inversedBy: channels
      joinTable:
        name: posts_channels
        joinColumns:
          channel_id:
            referencedColumnName: id
        inverseJoinColumns:
          post_id:
            referencedColumnName: id

Post:
  type: entity
  manyToMany:
    channels:
      targetEntity: Channel
      mappedBy: posts

The official documentation for this can be found here.

Related