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

[DoctrineBridge][Form] fix EntityChoiceList when indexing by primary foreign key #14372

Merged

Conversation

giosh94mhz
Copy link
Contributor

Q A
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets
License MIT
Doc PR

I've found a bug while using the 'entity' FormType.

Doctrine allow the definition of primary keys which are foreign key of other entities. In this scenario, the EntityChoiceList instance check if:

  • the entity has a id composed by a single column and
  • eventually, the column is an integer

When this happens, it use the primary key as "choices indices", but since is an entity it fails in many places, where it expects integer.

The easy solution is to check whether the single-column id is not an association. Anyway, I've fixed it the RightWay™ :), and now it resolve the entity reference to the actual column type, and restart the logic. Code speaks better then words.

@giosh94mhz giosh94mhz force-pushed the entity_choice_list_with_primary_key_association branch from f105cf7 to 54673f6 Compare April 16, 2015 13:03
@@ -41,6 +41,13 @@ class EntityChoiceList extends ObjectChoiceList
private $classMetadata;

/**
* Metadata for target class of primary key association.
*
* @var \Doctrine\Common\Persistence\Mapping\ClassMetadata
Copy link
Contributor

Choose a reason for hiding this comment

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

You can shorten this to

@var ClassMetadata

as the class is already imported in the namespace.

@webmozart
Copy link
Contributor

Thank you for the great PR! This looks good.

@giosh94mhz giosh94mhz force-pushed the entity_choice_list_with_primary_key_association branch from 54673f6 to aad5c12 Compare May 5, 2015 12:52
@giosh94mhz
Copy link
Contributor Author

Thank you for your feedback! I've fixed everything you've have spotted.

I agree with you that mixing variable initialization and return value was not clean, but I fixed the initIdentifierAs in another way. I've renamed it to getIdentifierInfoForClass, and simply return an array of [idField, idAsValue, idAsIndex]; I think it's clearer this way what the method does (and can be reused, if ever), and the list operator make it clear when and how the state is updated.

@giosh94mhz giosh94mhz force-pushed the entity_choice_list_with_primary_key_association branch 2 times, most recently from 080f312 to f6b8b1f Compare May 5, 2015 13:35
*
* @return array Return an array with identifier, idAsIndex and idAsValue
*/
private function getIdentifierInfoForClass(ClassMetadata $classMetadata)
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing docblock for function param.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @phansys , I'll fix it.

@giosh94mhz giosh94mhz force-pushed the entity_choice_list_with_primary_key_association branch from f6b8b1f to cde2087 Compare May 6, 2015 09:34
@giosh94mhz
Copy link
Contributor Author

Ok, I've applied all your suggestions. In addition there is a small comment to the new unit test class (if it's ok to have it, and is clear).

@webmozart webmozart added Acl Form and removed Acl labels Jun 16, 2015
list(
$this->idAsIndex,
$this->idAsValue
) = array_slice($this->getIdentifierInfoForClass($this->idClassMetadata), 1);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can paste the same code here as above instead of calling array_slice(), no? The ID field should remain the same.

For brevity, I still prefer a initIdentifierInfoForClass() method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The array_slice call is required (or a similar approach obviously :), since when called the second time the returned identifier is the one from the associated entity. The field name may be different in the two entities, so it is important that only the type information are carried on, without the foreign name.

I can refactor the method as initIdentifierInfoForClass, but still I have to do something like:

$bakIdField = $this->idField;
$this->initIdentifierInfoForClass($this->idClassMetadata); 
$this->idField = $bakIdField;

or like:

$this->initIdentifierInfoForClass($this->idClassMetadata, false); 
// ...
private function getIdentifierInfoForClass(ClassMetadata $classMetadata, $initIdField)
// ...

Which one is clearer? I vote for the second one...

Copy link
Contributor

Choose a reason for hiding this comment

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

I see. What about moving the $idField to the last position in the array then? Then you don't need array_slice().

@webmozart
Copy link
Contributor

Thanks! As you may have seen, the EntityChoiceList was replaced by a DoctrineChoiceLoader in Symfony 2.7. The appropriate class to fix it there is probably IdReader. Could you open a second PR for the 2.7 branch that fixes the same issue for the new implementation?

@giosh94mhz
Copy link
Contributor Author

I still have to dig into 2.7 sources, but I could give it try (since I have some spare time in the following days).

Anyway, should I fix this commit or throw it away since it will never be merged?

@webmozart
Copy link
Contributor

@giosh94mhz No, this will be merged too, but into the 2.3 branch. The other one goes into the 2.7 branch so that we don't have a regression with the new functionality.

Check the IdReader class, it should be pretty familiar to you.

@giosh94mhz
Copy link
Contributor Author

@webmozart That's good news! So I'll fix the 2.3 ASAP. For the 2.7 branch I'll check it in the next days (IdReader is familiar indeed).

@giosh94mhz giosh94mhz force-pushed the entity_choice_list_with_primary_key_association branch 2 times, most recently from 49a2f97 to ef2ab34 Compare June 19, 2015 13:14
@giosh94mhz
Copy link
Contributor Author

Ok, I've swapped the returned array and removed the array_slice call. No notice or other is triggered so it may be good as is.

This was the quickest solution, but I'm open to rename it as initIdentifierInfoForClass if you think is still foggy.

@webmozart
Copy link
Contributor

No, it's good now. Once the 2.7 PR is ready, I'll merge both in one go. Thanks! :)

@cursedcoder
Copy link

👍 @giosh94mhz I have the same issue using your bundle :)

@giosh94mhz
Copy link
Contributor Author

@cursedcoder Thank you for the feedback. First, make sure you checked out the branch entity_choice_list_with_primary_key_association (not master), then check if your code have a chain of foreign key defined in Doctrine. If this is the case, you may remove the chained entities (e.g. A[id], B[a_id==A.id], C[b_id==A.id==B.id]) with direct association (e.g. A[id], B[a_id==A.id], C[a_id==A.id]).
Otherwise, can you add a failing test?

@cursedcoder
Copy link

@giosh94mhz I'm not sure I understand you.

Just saying when using geonames bundle country entity I found the same bug.

@giosh94mhz
Copy link
Contributor Author

@cursedcoder aaah sorry. I though you found a case in which this fix didn't work by using my branch. GeonamesBundle didn't cross my mind (and definitely that bundle deserves some more love from me :)

@fabpot
Copy link
Member

fabpot commented Aug 12, 2015

Thank you @giosh94mhz.

@fabpot fabpot merged commit fe4246a into symfony:2.3 Aug 12, 2015
fabpot added a commit that referenced this pull request Aug 12, 2015
…by primary foreign key (giosh94mhz)

This PR was merged into the 2.3 branch.

Discussion
----------

[DoctrineBridge][Form] fix EntityChoiceList when indexing by primary foreign key

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

I've found a bug while using the 'entity' FormType.

Doctrine allow the definition of primary keys which are foreign key of other entities. In this scenario, the `EntityChoiceList` instance check if:
  * the entity has a id composed by a single column and
  * eventually, the column is an integer

When this happens, it use the primary key as "choices indices", but since is an entity it fails in many places, where it expects integer.

The easy solution is to check whether the single-column id is not an association. Anyway, I've fixed it the RightWay™ :), and now it resolve the entity reference to the actual column type, and restart the logic. Code speaks better then words.

Commits
-------

fe4246a [DoctrineBridge][Form] Fix EntityChoiceList when indexing by primary foreign key
fabpot added a commit that referenced this pull request Aug 12, 2015
…ry foreign key (giosh94mhz)

This PR was merged into the 2.7 branch.

Discussion
----------

[DoctrineBridge][Form] Fix IdReader when indexing by primary foreign key

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

Port to Symfony 2.7 of PR #14372 . The `IdReader` class can now resolve association and determine the real id value.

There is still room for improvement, though. Since I've added a `new IdReader` in the constructor, it is better to declare `IdReader` as `final` just to be safe.

PS: sorry to keep you waiting, @webmozart . When merging both commit don't forget to add the `@deprecated` annotation, and that `SingleAssociationToIntIdEntity.php` is duplicated.

Commits
-------

799d0e0 [DoctrineBridge][Form] Fix IdReader when indexing by primary foreign key
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.

None yet

5 participants