DDC-1439: Saving many to many association with composed keys. #1805

Closed
opened 2026-01-22 13:26:34 +01:00 by admin · 6 comments
Owner

Originally created by @doctrinebot on GitHub (Oct 20, 2011).

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user vigor_bg:

I have a problem with saving many to many realtion. My entities are.

calss Entity1 {

    /****
     * @Id @column
     */
    protected $key1;

    /****
     * @Id @column
     */
    protected $key2;

    /****
     * @ManyToMany (targetEntity="Entity2")
     * @JoinTable (name="Entity1Entity2",
     *      joinColumns={@JoinColumn(name="key1", referencedColumnName="key1")},
     *      inverseJoinColumns={@JoinColumn(name="key3", referencedColumnName="key3")}
     *      )
     */
    protected $entity2;
}

class Entity2 {

    /****
     * @Id @column
         * @GeneratedValue(strategy="AUTO")
     */
    protected $key3
}

So in the middle table that i keep the keys from the other 2 i need to save only key1 and key3. And key2 need to be ignored.
But when i try to save instead of saving the proper values it try to save the value for key2 instead of the value of key1 in the middle table.
After some debuging i found that in Doctrine\ORM\Persisters\ManyToManyPersister in the private function _collectJoinTableColumnParameters lies the problem.
Instead of taking the key by the name you are doing array_pop. And you are making a check for composite keys but you presume that bouth of the keys will be
in the middle table, but that is not the case for me.
The quick fix that i did was to remove the current check for isComposite and get the keys like we are in the isComposite case always.

Tanks in advance for the great work.

Have a nice day!

Originally created by @doctrinebot on GitHub (Oct 20, 2011). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user vigor_bg: I have a problem with saving many to many realtion. My entities are. ``` calss Entity1 { /**** * @Id @column */ protected $key1; /**** * @Id @column */ protected $key2; /**** * @ManyToMany (targetEntity="Entity2") * @JoinTable (name="Entity1Entity2", * joinColumns={@JoinColumn(name="key1", referencedColumnName="key1")}, * inverseJoinColumns={@JoinColumn(name="key3", referencedColumnName="key3")} * ) */ protected $entity2; } class Entity2 { /**** * @Id @column * @GeneratedValue(strategy="AUTO") */ protected $key3 } ``` So in the middle table that i keep the keys from the other 2 i need to save only key1 and key3. And key2 need to be ignored. But when i try to save instead of saving the proper values it try to save the value for key2 instead of the value of key1 in the middle table. After some debuging i found that in Doctrine\ORM\Persisters\ManyToManyPersister in the private function _collectJoinTableColumnParameters lies the problem. Instead of taking the key by the name you are doing array_pop. And you are making a check for composite keys but you presume that bouth of the keys will be in the middle table, but that is not the case for me. The quick fix that i did was to remove the current check for isComposite and get the keys like we are in the isComposite case always. Tanks in advance for the great work. Have a nice day!
admin added the Bug label 2026-01-22 13:26:34 +01:00
admin closed this issue 2026-01-22 13:26:35 +01:00
Author
Owner

@doctrinebot commented on GitHub (Oct 28, 2011):

Comment created by @beberlei:

This kind of mapping is not supported right now, the CLI tool "orm:validate-schema" should throw a compile time error on it as well.

I am not sure if just changing that line is enough for this to work, or if there are other obstacles as well.

@doctrinebot commented on GitHub (Oct 28, 2011): Comment created by @beberlei: This kind of mapping is not supported right now, the CLI tool "orm:validate-schema" should throw a compile time error on it as well. I am not sure if just changing that line is enough for this to work, or if there are other obstacles as well.
Author
Owner

@doctrinebot commented on GitHub (Oct 28, 2011):

Comment created by vigor_bg:

well that is the code so you can see what exactly i have removed

/****
     * Collects the parameters for inserting/deleting on the join table in the order
     * of the join table columns as specified in ManyToManyMapping#joinTableColumns.
     *
     * @param $coll
     * @param $element
     * @return array
     */
    private function _collectJoinTableColumnParameters(PersistentCollection $coll, $element)
    {
        $params = array();
        $mapping = $coll->getMapping();
        $isComposite = count($mapping['joinTableColumns']) > 2;

        $identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner());
        $identifier2 = $this->_uow->getEntityIdentifier($element);

       // if ($isComposite) {
            $class1 = $this->*em->getClassMetadata(get*class($coll->getOwner()));
            $class2 = $coll->getTypeClass();
        //}

        foreach ($mapping['joinTableColumns'] as $joinTableColumn) {
            if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) {
                //if ($isComposite) {
                    if ($class1->containsForeignIdentifier) {
                        $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])];
                    } else {
                        $params[] = $identifier1[$class1->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]];
                    }
//                } else {
//                    $params[] = array_pop($identifier1);
//                }
            } else {
                //if ($isComposite) {
                    if ($class2->containsForeignIdentifier) {
                        $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])];
                    } else {
                        $params[] = $identifier2[$class2->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]];
                    }
//                } else {
//                    $params[] = array_pop($identifier2);
//                }
            }
        }

        return $params;
    }
@doctrinebot commented on GitHub (Oct 28, 2011): Comment created by vigor_bg: well that is the code so you can see what exactly i have removed ``` /**** * Collects the parameters for inserting/deleting on the join table in the order * of the join table columns as specified in ManyToManyMapping#joinTableColumns. * * @param $coll * @param $element * @return array */ private function _collectJoinTableColumnParameters(PersistentCollection $coll, $element) { $params = array(); $mapping = $coll->getMapping(); $isComposite = count($mapping['joinTableColumns']) > 2; $identifier1 = $this->_uow->getEntityIdentifier($coll->getOwner()); $identifier2 = $this->_uow->getEntityIdentifier($element); // if ($isComposite) { $class1 = $this->*em->getClassMetadata(get*class($coll->getOwner())); $class2 = $coll->getTypeClass(); //} foreach ($mapping['joinTableColumns'] as $joinTableColumn) { if (isset($mapping['relationToSourceKeyColumns'][$joinTableColumn])) { //if ($isComposite) { if ($class1->containsForeignIdentifier) { $params[] = $identifier1[$class1->getFieldForColumn($mapping['relationToSourceKeyColumns'][$joinTableColumn])]; } else { $params[] = $identifier1[$class1->fieldNames[$mapping['relationToSourceKeyColumns'][$joinTableColumn]]]; } // } else { // $params[] = array_pop($identifier1); // } } else { //if ($isComposite) { if ($class2->containsForeignIdentifier) { $params[] = $identifier2[$class2->getFieldForColumn($mapping['relationToTargetKeyColumns'][$joinTableColumn])]; } else { $params[] = $identifier2[$class2->fieldNames[$mapping['relationToTargetKeyColumns'][$joinTableColumn]]]; } // } else { // $params[] = array_pop($identifier2); // } } } return $params; } ```
Author
Owner

@doctrinebot commented on GitHub (Oct 28, 2011):

Comment created by @beberlei:

i did find that already and also think its a good change to do sometihng along this lines (there is a better solution), however i dont knöow if that is even enough to get your use-case working across the whole ORM. We have zero test-cases for this kind of mapping, so i cannot guarantee you this works at all. This is why the orm:validate-schema gives you an error for this aswell (since some commits ago, this was missing earlier).

@doctrinebot commented on GitHub (Oct 28, 2011): Comment created by @beberlei: i did find that already and also think its a good change to do sometihng along this lines (there is a better solution), however i dont knöow if that is even enough to get your use-case working across the whole ORM. We have zero test-cases for this kind of mapping, so i cannot guarantee you this works at all. This is why the orm:validate-schema gives you an error for this aswell (since some commits ago, this was missing earlier).
Author
Owner

@doctrinebot commented on GitHub (Oct 31, 2011):

Comment created by @beberlei:

I checked the code again, this mapping is invalid. You have to define all id columns as join columns or otherwise the core will explode somewhere.

Since i cannot know and guarantee where it works and where it doesnt the SchemaValidator throws an exception about this problem. I added tests to master and 2.1.x that this mapping error is indeed given as an error.

@doctrinebot commented on GitHub (Oct 31, 2011): Comment created by @beberlei: I checked the code again, this mapping is invalid. You have to define all id columns as join columns or otherwise the core will explode somewhere. Since i cannot know and guarantee where it works and where it doesnt the SchemaValidator throws an exception about this problem. I added tests to master and 2.1.x that this mapping error is indeed given as an error.
Author
Owner

@doctrinebot commented on GitHub (Oct 31, 2011):

Issue was closed with resolution "Fixed"

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

@doctrinebot commented on GitHub (Oct 31, 2011):

Comment created by @beberlei:

sorry, i coudlnt fix it in 2.1.x, just in 2.2

@doctrinebot commented on GitHub (Oct 31, 2011): Comment created by @beberlei: sorry, i coudlnt fix it in 2.1.x, just in 2.2
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#1805