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

[RFC] Refactor population of object in serializer #1939

Closed
wants to merge 3 commits into from

Conversation

Toflar
Copy link
Contributor

@Toflar Toflar commented May 9, 2018

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

This is completely untested but I think it should be enough to outline the plan as described in #1937

Basically this would fix two things:

  • No more hardcoded dependency on id but using the IdentifiersExtractor instead.
  • It would actually fix Cannot POST id to create new resources #1937 because the serializers now test if the object even exists and only then disallow populating the object.

Looking for feedback here :-)

$identifiers = $this->identifiersExtractor->getIdentifiersFromResourceClass($class);
}

if (0 !== count(array_intersect(array_keys($data), $identifiers))) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have no clue if this works, it was just a quick hack and needs to be covered by tests. The idea here is just to check if the data contains all identifiers.

private function updateObjectToPopulate(array $data, array &$context)
{
try {
$context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getItemFromIri((string) $data['id'], $context + ['fetch_data' => true]);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This whole method was nonsense imho. Looks like it was developed before there was the IdentifiersExtractor.

Copy link
Member

Choose a reason for hiding this comment

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

Looks like it was developed before there was the IdentifiersExtractor.

It's the case.

Copy link
Contributor

Choose a reason for hiding this comment

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

You should add a deprecated error here saying this class is deprecated, and "unabstract" the AbstractItemNormalizer

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think that's a good plan. I'd keep it as is because the AbstractItemNormalizer is not used directly anywhere now and maybe there will be the day we have to add something to the ItemNormalizer. It can still be refactored for 3.0 even if there's no deprecation message.

*/
protected function getIdentifiersForDenormalization(string $class): array
{
return $this->identifiersExtractor->getIdentifiersFromResourceClass($class);
Copy link
Contributor

Choose a reason for hiding this comment

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

$identifierExtractor is nullable so you can't call a metod on it directly

@@ -65,6 +67,12 @@ public function __construct(PropertyNameCollectionFactoryInterface $propertyName
$this->setCircularReferenceHandler(function ($object) {
return $this->iriConverter->getIriFromItem($object);
});

if (null === $identifiersExtractor) {
@trigger_error(sprintf('Not passing an instance of IdentifiersExtractorInterface in "%s" is deprecated and will be mandatory in version 3.0. Not passing it is deprecated since 2.2.', __METHOD__), E_USER_DEPRECATED);
Copy link
Contributor

Choose a reason for hiding this comment

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

2.3 nay ? Kinda strange to introduce a deprecated on > 2.2.0...

Copy link
Contributor Author

@Toflar Toflar May 11, 2018

Choose a reason for hiding this comment

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

Not really, not being able to pass an id in a POST request is a bug to me but I don't care as long as it's published soon it can also go into 2.3 😄 Deprecating in bugfix releases is fine.


$context[self::OBJECT_TO_POPULATE] = $object;
}
}

This comment was marked as resolved.


$identifiers = $this->getIdentifiersForDenormalization($class);

if (0 !== count(array_intersect(array_keys($data), $identifiers))) {
Copy link
Member

Choose a reason for hiding this comment

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

I'm really not sure about this but maybe that performances would be better with a simple foreach test:

foreach ($identifiers as $identifier) { if (!isset($data[$identifier])) { return false; } }
return true;

Copy link
Member

Choose a reason for hiding this comment

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

Also it should be \count

Copy link
Member

Choose a reason for hiding this comment

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

Can be simplified to if (\count(array_intersect(array_keys($data), $identifiers))

* @param array $context
* @param array $identifiers
*/
protected function populateObjectIfAllowed($data, $class, array $context = [])
Copy link
Member

Choose a reason for hiding this comment

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

can't we type the $class as string?

@Toflar
Copy link
Contributor Author

Toflar commented May 10, 2018

Hey guys, this PR is not ready to review at all 😄 I mean, I know there's loads of stuff to cleanup, null pointer exceptions etc. but all I wanted to know is if you think the general concept could work and it's worth continuing into this direction or if I'm completely wrong. What I'm e.g. missing is the use of the IriConverter. What's the idea there. Asking the IriConverter first if there's a result and if not, asking the ItemDataProvider so both is supported?

@soyuka
Copy link
Member

soyuka commented May 11, 2018

The ItemDataProvider shouldn't be useful here. IMO IriConverter should be sufficient and we would need to merge #1837 before being able to fix this correctly.

@Toflar
Copy link
Contributor Author

Toflar commented May 11, 2018

@soyuka do you mean like so?

@soyuka
Copy link
Member

soyuka commented May 14, 2018

@Toflar yes this is really good!

@Toflar
Copy link
Contributor Author

Toflar commented May 14, 2018

Okay, phpunit is fixed. Behat left 😓

@Taluu
Copy link
Contributor

Taluu commented May 14, 2018

Sorry, couldn't help it. :D

@soyuka
Copy link
Member

soyuka commented May 14, 2018

Don't you run tests locally?

@Toflar
Copy link
Contributor Author

Toflar commented May 14, 2018

Sure but as it‘s squashed anyway I keep my motivation high by making smaller nonsense commits🤔😂

@Toflar Toflar changed the title [WIP] Refactor population of object in serializer [RFC] Refactor population of object in serializer May 15, 2018
@Toflar
Copy link
Contributor Author

Toflar commented May 15, 2018

Okay guysfolks, this is as far as I could get. It's finished. But some behat tests are left to fix which I can't get done because they are not isolated and it's mostly database schema errors. I'll leave it here again for the record: It's a horrible idea not to isolate and create a new schema for every test. You choose performance over DX.

@Toflar
Copy link
Contributor Author

Toflar commented May 15, 2018

@soyuka will try to help with the behat tests. I've restored them to the initial state as per his request.

@bendavies
Copy link
Contributor

@Toflar can you rebase on 2.2 and see how the tests look. I'm working on this and can PR to your branch. I'll raise some comments here.

return;
}

$updateAllowed = isset($context['api_allow_update']) && true !== $context['api_allow_update'];
Copy link
Contributor

@bendavies bendavies Jun 13, 2018

Choose a reason for hiding this comment

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

should be === not !== or just:
$updateAllowed = $context['api_allow_update'] ?? false;

return;
}

if (null !== $object && $updateAllowed) {
Copy link
Contributor

Choose a reason for hiding this comment

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

should be !$updateAllowed

* @param array $context
* @param array $identifiers
*/
protected function setObjectToPopulate($data, $class, array $context = [])
Copy link
Contributor

Choose a reason for hiding this comment

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

you need to pass $context by reference as you are modifying it.


$updateAllowed = isset($context['api_allow_update']) && true !== $context['api_allow_update'];
$identifiers = $this->getIdentifiersForDenormalization($class);
$writeableIdentifiers = [];
Copy link
Contributor

Choose a reason for hiding this comment

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

this logic is flawed unfortunately. You cannot validate the writability of identifiers here, as identifiers can be used to reference nested relations, to update them. example:
https://github.com/api-platform/core/blob/master/features/hal/max_depth.feature#L58

summary. having an identifier in the data does not mean it is trying to be updated.

$iri = implode(';', $identifiersData); // TODO: use $this->iriConverter->getIriFromPlainIdentifier() once https://github.com/api-platform/core/pull/1837 got merged.

try {
$object = $this->iriConverter->getItemFromIri($iri, $context + ['fetch_data' => true]);
Copy link
Contributor

Choose a reason for hiding this comment

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

exceptions thrown here should not be caught. the old code did not catch.

{
try {
$context[self::OBJECT_TO_POPULATE] = $this->iriConverter->getItemFromIri((string) $data['id'], $context + ['fetch_data' => true]);
} catch (InvalidArgumentException $e) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this has to be retained, it fixes #857

@bendavies
Copy link
Contributor

I have all the tests passing. I'll PR in the morning.

@Toflar
Copy link
Contributor Author

Toflar commented Jun 14, 2018

Closing in favor of #2022.

@Toflar Toflar closed this Jun 14, 2018
@Toflar Toflar deleted the fix-allow-post-id branch December 18, 2018 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants