Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ManyToManyPersister fails to remove join table entry if there is multiple join columns #6997

Merged
merged 2 commits into from
Jan 30, 2018

Conversation

NicolaF
Copy link

@NicolaF NicolaF commented Jan 19, 2018

This is due to a typo in \Doctrine\ORM\Persisters\Collection\ManyToManyPersister::getDeleteSQLParameters:

        foreach ($mapping['relationToSourceKeyColumns'] as $columnName => $refColumnName) {
            $params[] = isset($sourceClass->fieldNames[$refColumnName])
                ? $identifier[$sourceClass->fieldNames[$refColumnName]]
                : $identifier[$sourceClass->getFieldForColumn($columnName)];
        }

It should be $sourceClass->getFieldForColumn($refColumnName), as $columnName is the join table column, not the entity table column.

Copy link
Member

@Ocramius Ocramius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall good patch. Comments are really just nits, but we may need to check whether removal of the EntityManager#flush() call is possible. This is done elsewhere via UnitOfWork#registerManaged() (which may not even be needed here)

@@ -0,0 +1,103 @@
<?php
/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be dropped

/**
* @return int
*/
public function getId1(): int {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove all public API that isn't strictly needed. Use public properties instead

/**
* @return int
*/
public function getId(): int {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove all public API that isn't strictly needed

use Doctrine\ORM\Mapping\JoinColumn;
use Doctrine\ORM\Mapping\JoinTable;
/**
* Class ChildClass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant

use Doctrine\ORM\Mapping\JoinTable;
/**
* Class ChildClass
* @package Doctrine\Tests\Models\ManyToManyPersister
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant

@@ -0,0 +1,72 @@
<?php
/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be removed

@@ -0,0 +1,65 @@
<?php
/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be removed

$lastUpdate = array_pop($updates);

$this->assertEquals('DELETE FROM parent_child WHERE child_id1 = ? AND child_id2 = ?', $lastUpdate['query']);
$this->assertEquals([1,2], $lastUpdate['params']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using fixed assigned identifiers instead of AUTO_INCREMENT to prevent issues with subsequent test runs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Errata: I see you are already doing that 👍

/** @var ConnectionMock $conn */
$conn = $this->em->getConnection();

$updates = $conn->getExecuteUpdates();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if a full mock connection would be better here, assuming that we completely get rid of that EntityManager#flush() call

@NicolaF
Copy link
Author

NicolaF commented Jan 22, 2018

Hi @Ocramius,

I cleaned the test case as you requested. Howerver, I'm not sure to understand what you want to do with the EntityManager#flush() call. ManyToManyPersister#delete requires a PersistentCollection (because it needs the mapping to construct the delete statement), so UnitOfWork#registerManaged() won't do the job IMO.

Best Regards,
NicolaF

Copy link
Member

@lcobucci lcobucci left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just executed your tests without the fix and it passed, this doesn't look like an issue IMO...

@NicolaF
Copy link
Author

NicolaF commented Jan 23, 2018

@lcobucci Yes it looks like the test case is not correct. The original code in ManyToManyPersister#getDeleteSQLParameters is :

            $params[] = isset($sourceClass->fieldNames[$refColumnName])
                ? $identifier[$sourceClass->fieldNames[$refColumnName]]
                : $identifier[$sourceClass->getFieldForColumn($columnName)];

When I stumble upon this issue, isset($sourceClass->fieldNames[$refColumnName]) evaluated to false which caused the subsequent call to $sourceClass->getFieldForColumn($columnName)

which cannot work as $columnName is the column in the join table, not in the entity table.

I will investigate further and update the unit test to make it useful.

@NicolaF
Copy link
Author

NicolaF commented Jan 23, 2018

Test case updated. To actually reproduce the issue, we need :

  • multiple join columns
  • at least one join column where:
    • the join column and the referenced column have different names
    • the referenced column is a foreign key.

In this case:

  • the $referencedColumn is not in $sourceClass->fieldNames
  • ManyToManyPersister#getDeleteSQLParameters uses getFieldForColumn which was originally called with $columnName instead of $refColumnName
  • which throws if $columnName !== $refColumnName

@lcobucci
Copy link
Member

@NicolaF I will check it in a bit, can you please execute phpcbf on the files you created to ensure they follow our coding standard?

@lcobucci lcobucci self-requested a review January 23, 2018 11:00
@NicolaF
Copy link
Author

NicolaF commented Jan 23, 2018

@lcobucci done

@Ocramius
Copy link
Member

@NicolaF I think this is good to go. @lcobucci can you give it another check? This would need merging to 2.6.1, 2.7 and master

@lcobucci
Copy link
Member

@NicolaF I think this is good to go. @lcobucci can you give it another check? This would need merging to 2.6.1, 2.7 and master

@Ocramius I've made some minor changes to simplify the SUT and organise the annotations. It LGTM now. The build is failing for nightly PHP and mysql/pgsql but I think this is fine.

@lcobucci lcobucci merged commit c2f698e into doctrine:2.6 Jan 30, 2018
@lcobucci
Copy link
Member

@NicolaF 🚢 thanks!

@lcobucci
Copy link
Member

@NicolaF the good thing is that your test already passes in the current state of master, but I've added to the suite to ensure we won't have any regression (via 7928231).

@NicolaF
Copy link
Author

NicolaF commented Jan 30, 2018

You're welcome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants