DDC-551: Consider adding ability to specify additional join conditions on a @JoinTable / @JoinColumn #682

Closed
opened 2026-01-22 12:46:36 +01:00 by admin · 20 comments
Owner

Originally created by @doctrinebot on GitHub (Apr 28, 2010).

Originally assigned to: @asm89 on GitHub.

Jira issue originally created by user mjh_ca:

Per discussion with beberlei and romanb in #doctrine-dev yesterday, opening this ticket as a "feature request" to support migrating legacy schemas with a special many-to-many mapping to Doctrine.

Consider the following schema:

CREATE TABLE categories (
    category*id BIGINT UNSIGNED NOT NULL AUTO*INCREMENT,
    content_type ENUM('posts', 'videos'),
    /** ... **/
    PRIMARY KEY (category_id)
) ENGINE=InnoDB;

CREATE TABLE content*category*association (
    content_id BIGINT UNSIGNED NOT NULL,
    category_id BIGINT UNSIGNED NOT NULL,
    content_type ENUM('posts', 'videos'),
    PRIMARY KEY (content*id, category_id, content*type),
    FOREIGN KEY (category*id, content_type) REFERENCES categories(category_id, content*type) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB;

CREATE TABLE posts (
    post*id BIGINT UNSIGNED NOT NULL AUTO*INCREMENT,
    /** ... **/
    PRIMARY KEY (post_id)
) ENGINE=InnoDB;

CREATE TABLE videos (
    video*id BIGINT UNSIGNED NOT NULL AUTO*INCREMENT,
    /** ... **/
) ENGINE=InnoDB;

There is a Many-To-Many relationship between each of the posts and videos table (via the content_category_association table) to the categories table. The difference from a standard many-to-many relationship is there is an extra column in the association table (content_type) which must be included in the join condition to return correct results. Since both the videos and posts table have their own autonumber primary keys, a join against the association table must include an extra condition (i.e. INNER JOIN ... ON ... AND content_category_association.content_type = 'posts').

Perhaps you could allow passing of additional properties to @JoinTable / joinColumns to specify the additional join condition .. i.e.:

/*** @Entity **/
class Video
{
  /****
   * @ManyToMany(targetEntity="Category")
   * @JoinTable(name="content*category*association",
   *      joinColumns={@JoinColumn(name="content*id", referencedColumnName="video*id")},
   *      inverseJoinColumns={@JoinColumn(name="category*id", referencedColumnName="video*id")},
   *      extraJoinTerms={@JoinTerm(content_type="video")}
   *      )
   */
  private $categories;

  // ...
}

/*** @Entity **/
class Category
{
    // ...
}

Certainly this schema is not ideal from a pure OO perspective. Class inheritance with a discriminator column may have been a better way to do this, thereby allowing a globally unique "content_id" for all types of content, negating the need for the extra column in the association table. However, it would nonetheless be helpful to have this additional capability within Doctrine to avoid having to re-factor such a legacy schema.

Originally created by @doctrinebot on GitHub (Apr 28, 2010). Originally assigned to: @asm89 on GitHub. Jira issue originally created by user mjh_ca: Per discussion with beberlei and romanb in #doctrine-dev yesterday, opening this ticket as a "feature request" to support migrating legacy schemas with a special many-to-many mapping to Doctrine. Consider the following schema: ``` CREATE TABLE categories ( category*id BIGINT UNSIGNED NOT NULL AUTO*INCREMENT, content_type ENUM('posts', 'videos'), /** ... **/ PRIMARY KEY (category_id) ) ENGINE=InnoDB; CREATE TABLE content*category*association ( content_id BIGINT UNSIGNED NOT NULL, category_id BIGINT UNSIGNED NOT NULL, content_type ENUM('posts', 'videos'), PRIMARY KEY (content*id, category_id, content*type), FOREIGN KEY (category*id, content_type) REFERENCES categories(category_id, content*type) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB; CREATE TABLE posts ( post*id BIGINT UNSIGNED NOT NULL AUTO*INCREMENT, /** ... **/ PRIMARY KEY (post_id) ) ENGINE=InnoDB; CREATE TABLE videos ( video*id BIGINT UNSIGNED NOT NULL AUTO*INCREMENT, /** ... **/ ) ENGINE=InnoDB; ``` There is a Many-To-Many relationship between each of the posts and videos table (via the content_category_association table) to the categories table. The difference from a standard many-to-many relationship is there is an extra column in the association table (content_type) which must be included in the join condition to return correct results. Since both the videos and posts table have their own autonumber primary keys, a join against the association table must include an extra condition (i.e. INNER JOIN ... ON ... AND content_category_association.content_type = 'posts'). Perhaps you could allow passing of additional properties to @JoinTable / joinColumns to specify the additional join condition .. i.e.: ``` /*** @Entity **/ class Video { /**** * @ManyToMany(targetEntity="Category") * @JoinTable(name="content*category*association", * joinColumns={@JoinColumn(name="content*id", referencedColumnName="video*id")}, * inverseJoinColumns={@JoinColumn(name="category*id", referencedColumnName="video*id")}, * extraJoinTerms={@JoinTerm(content_type="video")} * ) */ private $categories; // ... } /*** @Entity **/ class Category { // ... } ``` Certainly this schema is not ideal from a pure OO perspective. Class inheritance with a discriminator column may have been a better way to do this, thereby allowing a globally unique "content_id" for all types of content, negating the need for the extra column in the association table. However, it would nonetheless be helpful to have this additional capability within Doctrine to avoid having to re-factor such a legacy schema.
admin added the Improvement label 2026-01-22 12:46:36 +01:00
admin closed this issue 2026-01-22 12:46:36 +01:00
Author
Owner
@doctrinebot commented on GitHub (Apr 28, 2010): - is duplicated by [DDC-1007: Add ability to filter a association](http://www.doctrine-project.org/jira/browse/DDC-1007) - is duplicated by [DDC-750: Named scopes (conditional columns and associations)](http://www.doctrine-project.org/jira/browse/DDC-750) - is duplicated by [DDC-1092: CustomTreeWalkers For Entities](http://www.doctrine-project.org/jira/browse/DDC-1092) - is duplicated by [DDC-1083: Support for a @Filter annotation to collections](http://www.doctrine-project.org/jira/browse/DDC-1083)
Author
Owner

@doctrinebot commented on GitHub (Nov 25, 2010):

Comment created by yaroslav:

Would be great to get this functionality

@doctrinebot commented on GitHub (Nov 25, 2010): Comment created by yaroslav: Would be great to get this functionality
Author
Owner

@doctrinebot commented on GitHub (Jul 22, 2011):

Comment created by @beberlei:

Assigned to asm89

@doctrinebot commented on GitHub (Jul 22, 2011): Comment created by @beberlei: Assigned to asm89
Author
Owner

@doctrinebot commented on GitHub (Jul 22, 2011):

Comment created by @beberlei:

Scheduled for 2.2

@doctrinebot commented on GitHub (Jul 22, 2011): Comment created by @beberlei: Scheduled for 2.2
Author
Owner

@doctrinebot commented on GitHub (Aug 16, 2011):

Comment created by @asm89:

I've been working on this ticket over here:
https://github.com/asm89/doctrine2/tree/DDC-551

Latest thing I added was the state of the collection of filters, because this is needed for parsing (and sometimes not parsing) the queries to generate SQL. I'd like some feedback about the state keeping. More information at the commit:
https://github.com/asm89/doctrine2/commit/2653d735e230d19b0fcb703800008f3abdb7a3ba

At this point the EntityManager keeps track of this state, but maybe it would be nice to have a separate FilterCollection keep track of the state/hashes etc?

@doctrinebot commented on GitHub (Aug 16, 2011): Comment created by @asm89: I've been working on this ticket over here: https://github.com/asm89/doctrine2/tree/[DDC-551](http://www.doctrine-project.org/jira/browse/DDC-551) Latest thing I added was the state of the collection of filters, because this is needed for parsing (and sometimes not parsing) the queries to generate SQL. I'd like some feedback about the state keeping. More information at the commit: https://github.com/asm89/doctrine2/commit/2653d735e230d19b0fcb703800008f3abdb7a3ba At this point the EntityManager keeps track of this state, but maybe it would be nice to have a separate FilterCollection keep track of the state/hashes etc?
Author
Owner

@doctrinebot commented on GitHub (Dec 18, 2011):

Comment created by @beberlei:

This issue is referenced in Github Pull-Request GH-224
https://github.com/doctrine/doctrine2/pull/224

@doctrinebot commented on GitHub (Dec 18, 2011): Comment created by @beberlei: This issue is referenced in Github Pull-Request GH-224 https://github.com/doctrine/doctrine2/pull/224
Author
Owner

@doctrinebot commented on GitHub (Dec 18, 2011):

Comment created by @beberlei:

Related Pull Request was closed: https://github.com/doctrine/doctrine2/pull/210

@doctrinebot commented on GitHub (Dec 18, 2011): Comment created by @beberlei: Related Pull Request was closed: https://github.com/doctrine/doctrine2/pull/210
Author
Owner

@doctrinebot commented on GitHub (Dec 18, 2011):

Comment created by @beberlei:

Related Pull Request was closed: https://github.com/doctrine/doctrine2/pull/224

@doctrinebot commented on GitHub (Dec 18, 2011): Comment created by @beberlei: Related Pull Request was closed: https://github.com/doctrine/doctrine2/pull/224
Author
Owner

@doctrinebot commented on GitHub (Dec 18, 2011):

Comment created by @beberlei:

Implemented

@doctrinebot commented on GitHub (Dec 18, 2011): Comment created by @beberlei: Implemented
Author
Owner

@doctrinebot commented on GitHub (Dec 18, 2011):

Issue was closed with resolution "Fixed"

@doctrinebot commented on GitHub (Dec 18, 2011): Issue was closed with resolution "Fixed"
Author
Owner

@doctrinebot commented on GitHub (Dec 18, 2011):

Comment created by @beberlei:

This issue is referenced in Github Pull-Request GH-225
https://github.com/doctrine/doctrine2/pull/225

@doctrinebot commented on GitHub (Dec 18, 2011): Comment created by @beberlei: This issue is referenced in Github Pull-Request GH-225 https://github.com/doctrine/doctrine2/pull/225
Author
Owner

@doctrinebot commented on GitHub (Dec 18, 2011):

Comment created by @beberlei:

Related Pull Request was closed: https://github.com/doctrine/doctrine2/pull/225

@doctrinebot commented on GitHub (Dec 18, 2011): Comment created by @beberlei: Related Pull Request was closed: https://github.com/doctrine/doctrine2/pull/225
Author
Owner

@doctrinebot commented on GitHub (Dec 19, 2011):

Comment created by @beberlei:

This issue is referenced in Github Pull-Request GH-227
https://github.com/doctrine/doctrine2/pull/227

@doctrinebot commented on GitHub (Dec 19, 2011): Comment created by @beberlei: This issue is referenced in Github Pull-Request GH-227 https://github.com/doctrine/doctrine2/pull/227
Author
Owner

@doctrinebot commented on GitHub (Dec 19, 2011):

Comment created by @beberlei:

Related Pull Request was closed: https://github.com/doctrine/doctrine2/pull/227

@doctrinebot commented on GitHub (Dec 19, 2011): Comment created by @beberlei: Related Pull Request was closed: https://github.com/doctrine/doctrine2/pull/227
Author
Owner

@doctrinebot commented on GitHub (Dec 19, 2011):

Comment created by darkangel:

Alex mentioned on IRC that filters do not provide the functionality that the OP requires, so this issue should really re-opened, unless I'm missing something?

@doctrinebot commented on GitHub (Dec 19, 2011): Comment created by darkangel: Alex mentioned on IRC that filters do not provide the functionality that the OP requires, so this issue should really re-opened, unless I'm missing something?
Author
Owner

@doctrinebot commented on GitHub (Dec 22, 2011):

Comment created by @beberlei:

This issue is referenced in Github Pull-Request GH-237
https://github.com/doctrine/doctrine2/pull/237

@doctrinebot commented on GitHub (Dec 22, 2011): Comment created by @beberlei: This issue is referenced in Github Pull-Request GH-237 https://github.com/doctrine/doctrine2/pull/237
Author
Owner

@superdav42 commented on GitHub (Apr 15, 2016):

@asm89 I don't see how this resolves the original issue. It requires adding conditions on the related join and filters don't seem to have access to that kind of information.

@superdav42 commented on GitHub (Apr 15, 2016): @asm89 I don't see how this resolves the original issue. It requires adding conditions on the related join and filters don't seem to have access to that kind of information.
Author
Owner

@vetalt commented on GitHub (Apr 27, 2016):

@superdav42
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/filters.html

@vetalt commented on GitHub (Apr 27, 2016): @superdav42 http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/filters.html
Author
Owner

@benface commented on GitHub (Jan 19, 2020):

I don't think the original issue is resolved. By looking at the source code, there is simply no hook that allows us to add conditions to the first JOIN generated by a many-to-many relationship.

@benface commented on GitHub (Jan 19, 2020): I don't think the original issue is resolved. By looking at the source code, there is simply no hook that allows us to add conditions to the first `JOIN` generated by a many-to-many relationship.
Author
Owner

@oojacoboo commented on GitHub (Apr 28, 2020):

@benface You are actually mostly correct here, it does not. But, with annotations hooked into your filters, you can add your own custom decorators and then add these in the where clause.

It's a nasty hack IMO and support for custom/additional join conditions should be supported. We just hit a similar issue with many:one relationship and soft-delete (actually archive, similar idea) on the one side of the relationship. If we had the ability to define an additional join condition, we could simply check that archived_at IS NULL and we'd be all good.

@oojacoboo commented on GitHub (Apr 28, 2020): @benface You are actually mostly correct here, it does not. But, with annotations hooked into your filters, you can add your own custom decorators and then add these in the where clause. It's a nasty hack IMO and support for custom/additional join conditions should be supported. We just hit a similar issue with many:one relationship and soft-delete (actually archive, similar idea) on the one side of the relationship. If we had the ability to define an additional join condition, we could simply check that `archived_at IS NULL` and we'd be all good.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#682