Skip to content

[DoctrineBridge][DoctrineExtractor] Exception in form validation for field with indexBy #37982

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

Closed
jkabat opened this issue Aug 28, 2020 · 15 comments

Comments

@jkabat
Copy link

jkabat commented Aug 28, 2020

Symfony version(s) affected: 4.4.11

Description
Form validation throws an exception on relation field which contains indexBy definition:
No mapping found for field 'foo_id' on class 'App\Entity\Foo'.

How to reproduce

    /**
     * @var Collection|Foo[]
     * @ORM\OneToMany(targetEntity="App\Entity\Foo", mappedBy="bar", indexBy="foo_id", cascade={"persist", "remove"}, orphanRemoval=true)
     * @Assert\Valid()
     */
    private $foos;

Possible Solution
When I change indexBy from "foo_id" to "foo" no exception is thrown. However indexBy will stop working as it suppose to.

Additional context
I have updated project from Symfony v.4.3.4 where it was working.

@xabbuh
Copy link
Member

xabbuh commented Aug 28, 2020

Can you please show the full stack text for this error?

@jkabat
Copy link
Author

jkabat commented Aug 28, 2020

Doctrine\ORM\Mapping\MappingException:
No mapping found for field 'bar_id' on class 'App\Entity\Foo'.

  at vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/MappingException.php:163
  at Doctrine\ORM\Mapping\MappingException::mappingNotFound('App\\Entity\\Foo', 'bar_id')
     (vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php:1258)
  at Doctrine\ORM\Mapping\ClassMetadataInfo->getAssociationMapping('store_id')
     (vendor/symfony/doctrine-bridge/PropertyInfo/DoctrineExtractor.php:116)
  at Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor->getTypes('App\\Entity\\Foo', 'foos', array())
     (vendor/symfony/property-info/PropertyInfoExtractor.php:110)
  at Symfony\Component\PropertyInfo\PropertyInfoExtractor->extract(object(RewindableGenerator), 'getTypes', array('App\\Entity\\Bar', 'foos', array()))
     (vendor/symfony/property-info/PropertyInfoExtractor.php:75)
  at Symfony\Component\PropertyInfo\PropertyInfoExtractor->getTypes('App\\Entity\\Bar', 'foos')
     (vendor/symfony/validator/Mapping/Loader/PropertyInfoLoader.php:68)
  at Symfony\Component\Validator\Mapping\Loader\PropertyInfoLoader->loadClassMetadata(object(ClassMetadata))
     (vendor/symfony/validator/Mapping/Loader/LoaderChain.php:54)
  at Symfony\Component\Validator\Mapping\Loader\LoaderChain->loadClassMetadata(object(ClassMetadata))
     (vendor/symfony/validator/Mapping/Factory/LazyLoadingMetadataFactory.php:124)
  at Symfony\Component\Validator\Mapping\Factory\LazyLoadingMetadataFactory->getMetadataFor(object(Bar))
     (vendor/symfony/validator/Validator/RecursiveContextualValidator.php:308)
  at Symfony\Component\Validator\Validator\RecursiveContextualValidator->validateObject(object(Bar), 'data', array('Default', 'CreateFoo'), 1, object(ExecutionContext))
     (vendor/symfony/validator/Validator/RecursiveContextualValidator.php:138)
  at Symfony\Component\Validator\Validator\RecursiveContextualValidator->validate(object(Bar), null, array('Default', 'CreateFoo'))
     (vendor/symfony/form/Extension/Validator/Constraints/FormValidator.php:104)
  at Symfony\Component\Form\Extension\Validator\Constraints\FormValidator->validate(object(Form), object(Form))
     (vendor/symfony/validator/Validator/RecursiveContextualValidator.php:767)
  at Symfony\Component\Validator\Validator\RecursiveContextualValidator->validateInGroup(object(Form), '000000003df7583000000000030dcd7b', object(ClassMetadata), 'Default', object(ExecutionContext))
     (vendor/symfony/validator/Validator/RecursiveContextualValidator.php:492)
  at Symfony\Component\Validator\Validator\RecursiveContextualValidator->validateClassNode(object(Form), '000000003df7583000000000030dcd7b', object(ClassMetadata), '', array('Default'), null, 1, object(ExecutionContext))
     (vendor/symfony/validator/Validator/RecursiveContextualValidator.php:315)
  at Symfony\Component\Validator\Validator\RecursiveContextualValidator->validateObject(object(Form), '', array('Default'), 1, object(ExecutionContext))
     (vendor/symfony/validator/Validator/RecursiveContextualValidator.php:138)
  at Symfony\Component\Validator\Validator\RecursiveContextualValidator->validate(object(Form), null, array('Default'))
     (vendor/symfony/validator/Validator/RecursiveValidator.php:93)
  at Symfony\Component\Validator\Validator\RecursiveValidator->validate(object(Form), null, null)
     (vendor/symfony/validator/Validator/TraceableValidator.php:66)
  at Symfony\Component\Validator\Validator\TraceableValidator->validate(object(Form))
     (vendor/symfony/form/Extension/Validator/EventListener/ValidationListener.php:50)
  at Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener->validateForm(object(PostSubmitEvent), 'form.post_submit', object(EventDispatcher))
     (vendor/symfony/event-dispatcher/EventDispatcher.php:264)
  at Symfony\Component\EventDispatcher\EventDispatcher->doDispatch(array(object(Closure), object(Closure)), 'form.post_submit', object(PostSubmitEvent))
     (vendor/symfony/event-dispatcher/EventDispatcher.php:239)
  at Symfony\Component\EventDispatcher\EventDispatcher->callListeners(array(object(Closure), object(Closure)), 'form.post_submit', object(PostSubmitEvent))
     (vendor/symfony/event-dispatcher/EventDispatcher.php:73)
  at Symfony\Component\EventDispatcher\EventDispatcher->dispatch(object(PostSubmitEvent), 'form.post_submit')
     (vendor/symfony/event-dispatcher/ImmutableEventDispatcher.php:44)
  at Symfony\Component\EventDispatcher\ImmutableEventDispatcher->dispatch(object(PostSubmitEvent), 'form.post_submit')
     (vendor/symfony/form/Form.php:671)
  at Symfony\Component\Form\Form->submit(array(), true)
     (vendor/symfony/form/Extension/HttpFoundation/HttpFoundationRequestHandler.php:109)
  at Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationRequestHandler->handleRequest(object(Form), object(Request))
     (vendor/symfony/form/Form.php:493)
  at Symfony\Component\Form\Form->handleRequest(object(Request))
     (src/Admin/Controller/StockPriceController.php:84)
  at App\Admin\Controller\StockPriceController->renderEditForm(object(Bar), object(Request))
     (src/Admin/Controller/StockPriceController.php:30)
  at App\Admin\Controller\StockPriceController->createForStock(object(Bar), object(Request))
     (vendor/symfony/http-kernel/HttpKernel.php:158)
  at Symfony\Component\HttpKernel\HttpKernel->handleRaw(object(Request), 1)
     (vendor/symfony/http-kernel/HttpKernel.php:80)
  at Symfony\Component\HttpKernel\HttpKernel->handle(object(Request), 1, true)
     (vendor/symfony/http-kernel/Kernel.php:201)
  at Symfony\Component\HttpKernel\Kernel->handle(object(Request))
     (public/index.php:27)                

@jkabat
Copy link
Author

jkabat commented Aug 29, 2020

This seems to be related: doctrine/orm#4203

@juanmiguelbesada
Copy link
Contributor

juanmiguelbesada commented Oct 15, 2020

Same issue here, in symfony 4.3.11 still works

@mickael-danjoux
Copy link

mickael-danjoux commented Oct 15, 2020

Hello, I have the same problem with a Collection of product prices that I would like to order by rate code id.
It works when I do product->getPrices() : all my prices are order by rate code id.
But when I use my product entity in a Symfony form, on submit I have this error:

No mapping found for field 'rate_code_id' on class 'App\Entity\Price'. while the dump result work perfectly

I use a Symfony 4.4.15

On my Product entity :

 /**
  * @ORM\OneToMany(targetEntity=Price::class, mappedBy="product", cascade={"persist"}, orphanRemoval=true,indexBy="rate_code_id", fetch="EAGER")
  */
   private Collection $prices;

On the Price Entity :

   /**
    * @ORM\ManyToOne(targetEntity=RateCode::class, fetch="EAGER")
    * @ORM\JoinColumn(nullable=false, name="rate_code_id",referencedColumnName="id")
    */
   private RateCode $rateCode; 

Thanks in advance if someone can help me

@juanmiguelbesada
Copy link
Contributor

juanmiguelbesada commented Oct 15, 2020

As a workaround you can use @Assert\DisableAutoMapping to disable AutoMapping validator on associations with custom indexBy.

BTW, it also fails on Symfony 4.3, but as AutoMapping is not enabled by default, the exception do not throw

@juanmiguelbesada
Copy link
Contributor

juanmiguelbesada commented Oct 15, 2020

I keep investigating. Is this related to #25834??

@xabbuh
Copy link
Member

xabbuh commented Oct 16, 2020

I tried to create a test case to reproduce the issue, but failed to do so. Could anyone of you create a small example application that allows to reproduce it?

@juanmiguelbesada
Copy link
Contributor

I'm on it

@juanmiguelbesada
Copy link
Contributor

juanmiguelbesada commented Oct 16, 2020

Here we go
https://github.com/juanmiguelbesada/symfony-issue-37982

EDIT: I reproduce it on symfony 5, but it fails on 4.3 and 4.4 as well

@xabbuh
Copy link
Member

xabbuh commented Oct 16, 2020

Can you confirm that #38604 fixes the issue?

@juanmiguelbesada
Copy link
Contributor

I'm not really sure it fixes complete the bug. In the repro project, applying the patch directly it still throw an exception.

Argument 1 passed to Symfony\Bridge\Doctrine\PropertyInfo\DoctrineExtractor::getPhpType() must be of the type string, null given, called in /Users/juanmi/Projects/Personal/symfony-issue-37982/vendor/symfony/doctrine-bridge/PropertyInfo/DoctrineExtractor.php on line 103

ClassMetadataInfo::getTypeOfField return null for non association fields, and then getPhpType fails

@xabbuh xabbuh reopened this Oct 19, 2020
@xabbuh
Copy link
Member

xabbuh commented Oct 19, 2020

Thank you for the feedback. I am going to investigate. There might be more edge cases to fix.

@juanmiguelbesada
Copy link
Contributor

Here is my PR #38628

@xabbuh can you review it?

Thanks

@juanmiguelbesada
Copy link
Contributor

@derrabus this bug is not fixed by that commit...

@derrabus derrabus reopened this Oct 20, 2020
nicolas-grekas added a commit that referenced this issue Nov 9, 2020
…lumns (juanmiguelbesada)

This PR was squashed before being merged into the 3.4 branch.

Discussion
----------

[DoctrineBridge]  indexBy could reference to association columns

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no <!-- please update src/**/CHANGELOG.md files -->
| Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
| Tickets       | Fix #37982 <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead -->
| License       | MIT
| Doc PR        | -
<!--
Replace this notice by a short README for your feature/bugfix. This will help people
understand your PR and can be used as a start for the documentation.

Additionally (see https://symfony.com/releases):
 - Always add tests and ensure they pass.
 - Never break backward compatibility (see https://symfony.com/bc).
 - Bug fixes must be submitted against the lowest maintained branch where they apply
   (lowest branches are regularly merged to upper ones so they get the fixes too.)
 - Features and deprecations must be submitted against branch 5.x.
-->

This is my approach to solve #37982. It partials reverts @xabbuh PR #38604

This is my first Symfony contribution, so please, tell me if I need to do something more or something is wrong.

Also, this bug affects 4.x and 5.x versions. I think merging in this branches is done automatically. If not, please tell me.

Thanks you

Commits
-------

f9a0e00 failing test for issue 38861
4c36145 [DoctrineBridge]  indexBy could reference to association columns
xabbuh added a commit that referenced this issue Jan 15, 2021
…" could be a db column name, for a referenced entity (victormacko)

This PR was merged into the 4.4 branch.

Discussion
----------

[DoctrineBridge] Take into account that indexBy="person_id" could be a db column name, for a referenced entity

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | none
| License       | MIT
| Doc PR        | bug-fix only

In Symfony 4.4.17 (I think), using ManyToMany in doctrine, along with indexBy="person_id" (in the related entity, which has a property of "id" (which in-turn uses the db column "person_id" worked as expected. When upgrading to Symfony 5.2.1, this stops working.

This change continues on from issue #37982 to fix a further edge case.

Commits
-------

472eab1 [DoctrineBridge] Take into account that indexBy="person_id" could be a db column name, for a referenced entity
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants