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

underscore in field name does not work with @Groups #1554

Closed
hariszukanovic opened this issue Dec 7, 2017 · 16 comments
Closed

underscore in field name does not work with @Groups #1554

hariszukanovic opened this issue Dec 7, 2017 · 16 comments

Comments

@hariszukanovic
Copy link

hariszukanovic commented Dec 7, 2017

I am using doctrine.orm.naming_strategy.underscore, in generated entities, the member variables are named in snake case.
When using @Groups to select fields, any of the fields with an underscore in their name are missing from the response.
Example: service_index
Any fields that are a single word (no underscore) do not exhibit this problem.
Also, when not using @Groups everything works just fine.

If variables are named in camel case, they appear in the response even when using @Groups and even if the column in the DB is still named in snake case.
Example: serviceIndex

v2.0.11 and v.2.1.4 tested.

@hariszukanovic
Copy link
Author

Am I the only one using underscores :) ?
Can anyone please confirm this is a known problem?

@colinedmz
Copy link

@hariszukanovic : I have the same problem

@netsuo
Copy link

netsuo commented Apr 1, 2018

@hariszukanovic I have the same problem and it even weirder.

Suppose you have a field name entity_id. With @groups, as you stated, it is not returned.

Now, do this:

    /**
     * @ORM\Column(type="integer")
     * @var int
     */
    private $entity_id;


    /**
     * @Groups("get")
     * @var int
     */
    private $entityId;

Notice I just duplicated the variable and set the second camelCase one to @groups and removed the group on the first.

Result: your variable is on the api response named like the second variable even though the colum getter is still on the snake_case one

{
  "@context": "/contexts/Test",
  "@id": "/tests",
  "@type": "hydra:Collection",
  "hydra:member": [
    {
      "@id": "/tests/1",
      "@type": "Test",
      "entityId": 1871912
   }
}

@rubiodamian
Copy link

Same to me. Super weird bug.

@hariszukanovic
Copy link
Author

hariszukanovic commented May 1, 2018 via email

@Simperfit Simperfit added the bug label May 2, 2018
@Simperfit
Copy link
Contributor

Could you please provide a little project based on https://github.com/api-platform/api-platform in order to test this behaviour ?

@rubiodamian
Copy link

@Simperfit if any field of the ApiResource entity has an underscore "_" the field will be ReadOnly. Is simple as that.

@teohhanhui
Copy link
Contributor

I suspect this has to do with how the Symfony PropertyInfo component extracts accessor and mutator methods, specifically:

https://github.com/symfony/symfony/blob/v4.1.0/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php#L287

https://github.com/symfony/symfony/blob/v4.1.0/src/Symfony/Component/PropertyInfo/Extractor/ReflectionExtractor.php#L320

If your property name does not follow the convention, you'd have to add the missing property metadata yourself.

@hariszukanovic
Copy link
Author

Would you have a working example of this (should I call it workaround?)?

@Cethy
Copy link
Contributor

Cethy commented Sep 17, 2018

I have the same problematic behavior w/ fields prefixed with is* (like isActive) :

  • the @Groups does not work (field doesn't show in the response) ;
  • Swagger correctly displays the expected documentation ;
  • but when group strategy removed, it shows as a field name active in the response ;

If you remove the group strat too, I guess you'll see your field camelized.

EDIT: woops, never mind, i messed up with the getter method.

@soyuka
Copy link
Member

soyuka commented Apr 7, 2019

closing, use custom metadata if the behavior differs from the one expected by the property info as @teohhanhui said

@erikkubica
Copy link

Even weirder behaviour is that if it´s camel case, api platform returns array of IRIs if its snake case it returs list of objects in some places and in some scenarios and in some it returns objects. My project takes like 2x longer with api platform than without it. Took my few hours to figure out the underscore thing, fixed part of my code, but broke the other part. So i have no idea what to do.

@dobrescucristian
Copy link

I'm not sure what the "custom metadata" solutions refers to, that only lead me to SQL errors because the DB columns are still "some_thing" and setting the name property in @Orm\Column to "someThing" broke the queries.

But if you find this issue on google because you're naming your Entity properties "some_thing" and they don't show in your API responses, it's because you need to name them "someThing".

If you want "some_thing" in the the API response, do this:

image

From the docs: https://api-platform.com/docs/core/serialization/#name-conversion

@TeLiXj
Copy link

TeLiXj commented Jan 27, 2021

Using camel case in variables only is mandatory to develop Symfony bundles or help in the Symfony project. If your project follow PSR-12 (PSR-2 is deprecated) you can choose what do you want for variable names. I always choose snake case and have use a lot of different bundles without problems since Symfony 2, and reflection works fine in all cases.
I don't know how this issue can be closed saying "if the behavior differs from the one expected by the property info" because is a completely correct way to define a entity in Symfony and Doctrine. I hope that you can reconsider your action and try to fix it.
Meanwhile, if you have this problem you can use the groups directly in the setters o getters involved.
You must change from

/**
 * @ORM\Column(type="int")
 * @Groups({"baz:read", "baz:write"})
 */
private $foo_bar;

to

/**
 * @ORM\Column(type="int")
 */
private $foo_bar;

/**
 * @Groups({"baz:read"})
 */
public function getFooBar(): ?int
{
    return $this->foo_bar;
}

/**
 * @Groups({"baz:write"})
 */
public function setFooBar(int $foo_bar): self
{
    $this->foo_bar = $foo_bar;

    return $this;
}

@evotodi
Copy link

evotodi commented Apr 9, 2021

By the way another workaround is a modification to @TeLiXj answer because for me rewriting annotations for 207 properties plus getters and setters was looking like a chore.

/**
* @ORM\Column(type="boolean")
* @Groups({"dataspec:read","dataspec:write"})
*/
private bool $g_heEn = true;

Changed getters and setters from:

public function isGHeEn(): bool
{
	return $this->g_heEn;
}

public function setGHeEn(bool $g_heEn): self
{
	$this->g_heEn = $g_heEn;
	return $this;
}

To (notice the underscore placement):

public function isG_HeEn(): bool
{
	return $this->g_heEn;
}

public function setG_HeEn(bool $g_heEn): self
{
	$this->g_heEn = $g_heEn;
	return $this;
}

And then docs post example shows:

{
  "g_heEn": true
}

And return example shows:

{
  "g_heEn": true,
}

Which is what i was after.
Yes it is not PSR-12 or PSR-2 and it is snakeCamel but when in a pinch.

@TeLiXj
Copy link

TeLiXj commented Jun 28, 2021

Finally, after update to PHP 8 and move from annotations to attributes works at least on read

nicolas-grekas added a commit to symfony/symfony that referenced this issue Sep 29, 2023
… isReadable() when checking snake_case properties (jbtronics)

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

Discussion
----------

[PropertyInfo] Make isWriteable() more consistent with isReadable() when checking snake_case properties

| Q             | A
| ------------- | ---
| Branch?       | 6.4
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Related with issues #29933 and #16889
| License       | MIT
| Doc PR        |

Currently PropertyInfo is a bit inconsistent in the behavior of isWriteable() and isReadable() when using it with properties in snake_case format, which are only accessible via a getter and setter. To be readable the getter function has to be in camel_case (e.g. `getSnakeCase()`), while the setter function has to remain in the snake case format (e.g. `setSnake_case()`).

In this example class:
```php
class Dummy {
  private string $snake_case;

  public function getSnakeCase(): string
  { }

  public function setSnakeCase(string $snake_case): void
  { }

}
````

the property `snake_case` is considered readable like you would expect, but not writeable, even though the property has a useable setter and the value can be actually be modified fine by something like the PropertyAccess component.
To make the property actually considered writeable, the setter would need to be named `setSnake_case`, which is pretty inconsistent with the behavior of isReadable and makes it very hard to use this component on snake_case properties.

This inconsistencies are caused by the fact, that `isReadable` in ReflectionExtractor uses the `getReadInfo()` function which does a camelization of the property name internally, while the `isWriteable()` function uses the `getMutatorMethod()` function which just perform a capitalization of the first letter.

This PR fixes this inconsistencies between isReadable() and isWriteable() by allowing to use a camelCase style setter to be considered writeable, as this is much more common then to use the mix of snake and camelCase currently required.

The getWriteInfo() function is not useable here, because it needs both a add and remove Function on collection setters to give proper infos, while the current `isWriteable()` implementation considers a class with just a add or a remove function as writeable. Therefore the property name just gets camelized before being feed into the `getMutatorMethod()`, which gives the desired result.

To maximize backwards compatibility, if no camelCase style setter is found, it is still checked for a snake_case setter, so that classes having these, are still considered writeable after the change.

The current behavior is causing some inconsistencies in higher level libraries, which rely on this component. In API Platform for example properties in snake_case are considered read only even though they have a (camel case) setter, because of this. See issue: api-platform/core#5641 and api-platform/core#1554

Commits
-------

7c9e6bc [PropertyInfo] Make isWriteable() more consistent with isReadable() when checking snake_case properties
symfony-splitter pushed a commit to symfony/property-info that referenced this issue Sep 29, 2023
… isReadable() when checking snake_case properties (jbtronics)

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

Discussion
----------

[PropertyInfo] Make isWriteable() more consistent with isReadable() when checking snake_case properties

| Q             | A
| ------------- | ---
| Branch?       | 6.4
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | Related with issues #29933 and #16889
| License       | MIT
| Doc PR        |

Currently PropertyInfo is a bit inconsistent in the behavior of isWriteable() and isReadable() when using it with properties in snake_case format, which are only accessible via a getter and setter. To be readable the getter function has to be in camel_case (e.g. `getSnakeCase()`), while the setter function has to remain in the snake case format (e.g. `setSnake_case()`).

In this example class:
```php
class Dummy {
  private string $snake_case;

  public function getSnakeCase(): string
  { }

  public function setSnakeCase(string $snake_case): void
  { }

}
````

the property `snake_case` is considered readable like you would expect, but not writeable, even though the property has a useable setter and the value can be actually be modified fine by something like the PropertyAccess component.
To make the property actually considered writeable, the setter would need to be named `setSnake_case`, which is pretty inconsistent with the behavior of isReadable and makes it very hard to use this component on snake_case properties.

This inconsistencies are caused by the fact, that `isReadable` in ReflectionExtractor uses the `getReadInfo()` function which does a camelization of the property name internally, while the `isWriteable()` function uses the `getMutatorMethod()` function which just perform a capitalization of the first letter.

This PR fixes this inconsistencies between isReadable() and isWriteable() by allowing to use a camelCase style setter to be considered writeable, as this is much more common then to use the mix of snake and camelCase currently required.

The getWriteInfo() function is not useable here, because it needs both a add and remove Function on collection setters to give proper infos, while the current `isWriteable()` implementation considers a class with just a add or a remove function as writeable. Therefore the property name just gets camelized before being feed into the `getMutatorMethod()`, which gives the desired result.

To maximize backwards compatibility, if no camelCase style setter is found, it is still checked for a snake_case setter, so that classes having these, are still considered writeable after the change.

The current behavior is causing some inconsistencies in higher level libraries, which rely on this component. In API Platform for example properties in snake_case are considered read only even though they have a (camel case) setter, because of this. See issue: api-platform/core#5641 and api-platform/core#1554

Commits
-------

7c9e6bc36e [PropertyInfo] Make isWriteable() more consistent with isReadable() when checking snake_case properties
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