Reputation: 87
We are upgrading a web API from:
To:
We have a large main entity, with many Collection
objects, containing other entities. We accept new instances via a POST
call, with a JSON body, that gets deserialized by JMS Serializer before eventually being inserted to the database. The Collection
objects are represented by JSON arrays, of course.
After inserting the main entity, we flush
, refresh
, and findOneBy
the entity back from the DB again, to return to the client. I believe the reason for this was to get back exactly what would be returned in a future GET
request. In the old version, this worked.
EDIT TO ADD
It turns out that this was not actually working as I thought it was, in the pre-upgrade version, but that something else was going on. We have this unusual pattern, where we have two separate Doctrine connections, one for "read" and one for "write". Since this has always seemed like a useless complexity, we had taken advantage of the upgrade process to go ahead and simplify that, so one connection would be used everywhere. Yes, it was a bad idea to mix in more changes than necessary at this time, and we got stung by that.
In our pre-upgrade branch, the write-connection was used to write the
new entity to the DB, but the read-connection was used to read it back
again. It appears the copy of the entity with the null
values could
not be shared between the two separate Doctrine connections, so the
read-connection returned a fresh copy from the DB with appropriate
empty collections.
This is obviously over-complicated and must have at least some performance penalty, but we won't be given time to make deeper changes at this point, so we've put it back that way. It's surprising and disappointing to me, if true, that Doctrine just can't do this without this hack.
END EDIT TO ADD
On our upgraded branch, we have noticed that when the POST
is missing a property that that represents a Collection
, the instance that we get to return to the client has null
instead of an empty collection. In fact, on investigation, I find that if I ===
compare the instance we inserted with persist
to the one returned from findOneBy
, they are the same object. I would expect the intervening refresh
call to prevent that, on the upgraded branch, it does not. On the old branch, we do get back an empty Collection
for the property.
I have read that the official position of Doctrine is that collections should always be initialized in the constructor. That makes perfect sense to me! However, the JMS Serializer doesn't use the constructor - these objects are never constructed before being passed to Doctrine to store, they are just deserialized.
JMS Serializer, on the other hand, doesn't seem to provide any way to set a Collection
as a default for a missing property, or to run a constructor.
I'm open to any suggestions, here, but I would prefer to fix this by restoring the Doctrine behavior we see on our old branch, of getting a new instance of the Entity from the DB. The reason this would be preferable is that this is already a complicated and high-risk upgrade, and I want to minimize any additional risk of something else, unanticipated, being different, and breaking our clients.
Upvotes: 1
Views: 74
Reputation: 87
I've found an answer, that is what we, at least, will be going forward with for now. It may actually be impossible to do what is stated in the question, I do not know, so I'm still hoping somebody comes up with a better answer, out of curiosity if nothing else.
This can be done using two separate connections, connection-A to write
the new entity to the DB, but connection-B to read it back again. The
copy of the entity with the null
values cannot be shared between the
two separate Doctrine connections, so connection-B will return a fresh
copy from the DB with appropriate empty collections.
I don't recommend using separate connections. If possible, it would be best to fix the underlying design so that this isn't necessary, but sometimes a bad solution is better than no solution.
Upvotes: 0