foundry
foundry copied to clipboard
Issue with Proxy Entities in Version 2
Hello,
I recently migrated to version 2.
In the following code, I encountered the error below. This seems to be due to V2 now using proxies.
$post = PostFactory::createOne();
// logic with post...
$comment = CommentFactory::createOne(['post' => $post]);
// logic for test...
// I use browser :
$this->browser()
->get(sprintf('/api/post/%s', $post->getId()))
->assertStatus(200)
; // Without this test using browser, I don't get the error
$comment->setContent('content');
$comment->_save(); // Error here
Error:
* A new entity was found through the relationship 'App\Entity\Comment#post' that was not configured to cascade persist operations for entity: App\Entity\Post@5238. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'App\Entity\Post#__toString()' to get a clue.
How can this be modified in this case? I didn't have this error in V1.
Thank you.
More details :
Work :
$post = PostFactory::createOne();
CommentFactory::createOne([
'post' => $post,
]);
$this->browser()
->get(sprintf('/api/post/%s', $post->getId()))
->assertStatus(200)
;
Work :
$post = PostFactory::createOne();
CommentFactory::createOne([
'post' => $post,
]);
$comment->setContent('content');
$comment->_save();
The error seems to come from calling via browser by sending $post THEN wanting to modify $comment, but why?
Hi,
do you modify the post in your functional test with browser?
what happens if you do this?
$comment->_withoutAutoRefresh(function (Post $post) {
$comment->setContent('content');
});
$comment->_save();
No, and this work too :
$comment = CommentFactory::createOne(['post' => PostFactory::createOne()]);
$this->browser()
->get(sprintf('/api/post/%s', $comment->getPost()->getId()))
->assertStatus(200)
;
$comment->setContent('content');
$comment->_save();
I don't get the difference between the last working version and the failing one?
what happens if you do this?
$comment->_withoutAutoRefresh(function (Post $post) { $comment->setContent('content'); }); $comment->_save();
I have this error :
TypeError: Value of type null returned from AppEntityCommentProxy::__get() must be compatible with unset property AppEntityCommentProxy::$_autoRefresh of type bool
I don't get the difference between the last working version and the failing one?
The only difference between the code that works and the code that doesn't is that I declare post directly in the comment.
If I instantiate it in a variable it fails.
Don't work :
$post = PostFactory::createOne();
$comment = CommentFactory::createOne(['post' => $post]);
$this->browser()
->get(sprintf('/api/post/%s', $post->getId()))
->assertStatus(200)
;
$comment->setContent('content');
$comment->_save();
Work :
$comment = CommentFactory::createOne(['post' => PostFactory::createOne()]);
$this->browser()
->get(sprintf('/api/post/%s', $comment->getPost()->getId()))
->assertStatus(200)
;
$comment->setContent('content');
$comment->_save();
Je viens de voir que tu étais français donc je décris mon problème dans cette langue, je pense que je serai mieux compris.
En fait j'ai l'impression que d'extraire "comment" dans une variable, ce qui en fait donc un proxy, pose problème avec browser pour une raison que j'ignore.
Ce qui est étrange, c'est qu'il y a plusieurs scénarios qui fonctionnent, mais un en particulier pose soucis. C'est quand je créé un "comment" dans une variable, l'affecte ensuite à "post", puis execute un appel à browser. Dans ce cas là, si j'essaie de modifier "comment", l'erreur se produit.
Encore une fois, si je n'extrais pas "comment" dans une variable, je n'ai aucune erreur.
Also works when we access $comment in browser via $post->getComment()->getId() :
$post = PostFactory::createOne();
$comment = CommentFactory::createOne(['post' => $post]);
$this->browser()
->get(sprintf('/api/post/%s', $comment->getPost()->getId()))
->assertStatus(200)
;
$comment->setContent('content');
$comment->_save();
I'm having the same issue, with this (simplified):
public function testIssue1(): void
{
$event = EventFactory::createOne();
$event->setLocation(LocationFactory::createOne());
$event->_save();
}
// Doctrine\ORM\ORMInvalidArgumentException: A new entity was found through the relationship 'App\Entity\Event#location'
public function testIssue2(): void
{
$event = EventFactory::createOne();
$event->_withoutAutoRefresh(function (Event $event): void {
$event->setLocation(LocationFactory::createOne());
});
$event->_save();
}
// TypeError: Cannot assign null to property AppEntityEventProxy::$_autoRefresh of type bool
Hi @mmarton
I'm wondering if both problems are the same... Could you give me the whole error message, please? (from your first example)
I think your problem will be solved by passing LocationFactory::createOne()->_real()
We're gonna advertise on the docs for this problem
Sure, here is the full stack:
Foundry (Tests\Integration\Foundry)
✘ Orm issue
┐
├ Doctrine\ORM\ORMInvalidArgumentException: A new entity was found through the relationship 'App\Entity\Event#location' that was not configured to cascade persist operations for entity: Rutherford Group. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example @ManyToOne(..,cascade={"persist"}).
│
│ /var/www/html/vendor/doctrine/orm/src/ORMInvalidArgumentException.php:103
│ /var/www/html/vendor/doctrine/orm/src/UnitOfWork.php:3859
│ /var/www/html/vendor/doctrine/orm/src/UnitOfWork.php:417
│ /var/www/html/vendor/doctrine/orm/src/EntityManager.php:403
│ /var/www/html/vendor/zenstruck/foundry/src/Persistence/PersistenceManager.php:197
│ /var/www/html/vendor/zenstruck/foundry/src/Persistence/PersistenceManager.php:171
│ /var/www/html/vendor/zenstruck/foundry/src/Persistence/IsProxy.php:60
│ /var/www/html/tests/Integration/FoundryTest.php:22
┴
While don't know for sure if I've had the same issue as @fouteox, I have had the issue brought up by @mmarton. I fixed it by using _real(). For example:
$contact = ContactFactory::createOne([
'firstName' => 'Alyson',
'lastName' => 'Perkins',
'mainEmail' => null,
'mainPhone' => null,
])
$accountContact = AccountContactFactory::createOne([
'contact' => $contact,
'account' => AccountFactory::new([
'name' => 'Rhode Island',
'uid' => 'RI',
]),
'main' => true,
]);
$contact->addAccountContact($accountContact->_real());
// this blows up with the Doctrine error without calling _real() above
$contact->_save();
I've just created a PR to mitigate this problem (note: it won't work if the setter / adder uses fluent api, due to some restrictions in symfony/var-exporter). It still might occur in some other edge cases, I've also added a not in the docs about it
https://github.com/zenstruck/foundry/pull/635
I've just created a PR to mitigate this problem (note: it won't work if the setter / adder uses fluent api, due to some restrictions in symfony/var-exporter). It still might occur in some other edge cases, I've also added a not in the docs about it
#635
Yes I can confirm that the code below works. That is to say by having a setter which returns not static but void. But is it that we need to double setter for the tests to work?
$post = PostFactory::createOne();
$comment = CommentFactory::createOne(['post' => $post]);
$this->browser()
->get(sprintf('/api/post/%s', $post->getId()))
->assertStatus(200)
;
$comment->setContentWithoutStatic('content'); // Here, setter return void instead static
$comment->_save();
Creating new public methods specific for the tests is definitely not what you need to do :sweat_smile: maybe you could try:
$comment->_real()->setContent('content');
or maybe try another time _withoutAutoRefresh(), we've fixed the problem in v2.0.2
$comment->_withoutAutoRefresh(function (Post $post) {
$comment->setContent('content');
});
$comment->_save();
side note: I must say I still don't understand why a functional test here would create an error. If you manage to create a reproducer project, I'd be happy to debug that.
I unfortunately did not find the time to create a reproduction project but our various exchanges allowed me to understand how to no longer have the errors.
Thank you.
I will come back here if I am faced with the problem again or if I have time to create a