Error with Symfony Serializer can't normalize int to float - php

I get an error when I try to denormalize an object with a float attribute but an int value.
Symfony\Component\Serializer\Exception\NotNormalizableValueException:
The type of the "solde" attribute for class
"Wallet" must be one of "float" ("int"
given).
My Serializer configuration:
class SerializerService implements SerializerInterface, NormalizerInterface, DenormalizerInterface
{
public const JSON_FORMAT = 'json';
/**
* #var Serializer
*/
private $serializer;
/**
* #var LoggerInterface
*/
private $logger;
/**
* #param LoggerInterface $logger
*/
public function __construct(
LoggerInterface $logger
) {
$this->logger = $logger;
$this->serializer = new Serializer([
new DateTimeNormalizer(),
new ObjectNormalizer(null, null, null, new ReflectionExtractor()),
new ArrayDenormalizer()
], [
new JsonEncoder(
new JsonEncode(),
new JsonDecode(),
)
]);
}
...
...
I didn't find anything relevant in the Symfony Serializer documentation.
Can you help me please?

The problem is not so much with the serializer, but your data. You probably have something like:
{
“solde”: 1.0
}
but it should be: {“solde”: 1}, i.e. your data should be an int and not a float.
If you make $solde in your Wallet-object a float, instead of an int you won’t have this problem because converting from int to float is usually lossless, i.e. 1 to 1.0. Also, you should not accept floats for this kind of data in the first place. Assuming “1.0” stands for something like 1$ or 1 EUR, then it is customary to instead save it as a string or int as cent value, i.e. 100 instead and I would recommend also using this format for your JSON. That way, you don’t need floats and you won’t run into weird floating point issues. I highly recommend going that route. Since you are doing the conversion the other way (from float to int) you will lose precision, i.e. 1.5 will either become 1 or 2, which obviously is bad because your users might not know which of the 2 possible outcomes it will be.
Coming back to your error. In your object you can resolve this type issue either when you receive the data, i.e. set the field from JSON or whenever you read the data. For the former do the lossy conversion (casting to int, rounding, …) inside the object’s setter for this field. You might need to adjust your ObjectNormalizer to use the getters and setters for this, but it should use the PropertyAccess-component by default which should use the setter before it looks for the property, if I am not mistaken. It would roughly look like this:
class Wallet
{
private int $solde;
// …
public function setSolde(float|int $solde): void
{
$this->solde = (int) $solde; // casting to int will just cut off anything after the “.” in floats
}
}
You could also have your field be either float or int and then do the conversion on the getter, i.e. when you use the field but then you might get issues when you want to persist the data in the database.
class Wallet
{
private int|float $solde;
// …
public function getSolde(): int
{
return (int) $this->solde; // casting to int will just cut off anything after the “.” in floats
}
}
If you don’t want to adjust your object for this, then you can teach the Serializer to do the lossy conversion by writing a custom denormalizer (Symfony combines denormalizers and normalizers in their normalizers, i.e. ObjectNormalizer, if you are looking for a reference how to write them). This denormalizer will then handle your object, i.e. Wallet, and manually map each field from your JSON to the object’s field, including “solde”, where you have the type error. You can then resolve the type error in the Denormalizer by doing the lossy conversion (casting to int, rounding, …). You have to be careful to not make the Denormalizer too generic as you might end up doing this even in places where you don’t want this. Symfony has a part in the docs for custom normalizers/denormalizers, so it should be fairly straightforward. If you really need this, I would recommend going this route instead, assuming you only need to worry about this problem when reading the data from JSON.

Related

Convert single property for response with the FOSRestBundle in Symfony 5

I'm working with numbers with a lot of decimal places in my symfony application. In my doctrine entity I have for example this property:
/**
* #ORM\Column(type="float")
*/
private float $value;
In my mysql database I have this value for example: 0.00000000020828579949508
When I dump that in PHP I'm getting this: float(9.3722658865184E-7). I also made an API with the FOSRestBundle. In that API I want to return the value not in exponential form with at least 12 of it's decimal places. I think in that case I have to provide the value as string, correct? I figured out that I can convert it to string with something like this: sprintf("%.12f", $myEntity->getValue()). But I have two questions now:
How can I convert a single property for response with the FOSRestBundle? So that I return the "value" property as string, even if it is a float normally.
Is there a general best practice or any tips to work with such numbers in symfony, doctrine and the FOSRestBundle?
Right now this is my controller action:
public function getData(): Response
{
$repository = $this->getDoctrine()->getRepository(MyEntity::class);
$data = $repository->findAll();
return $this->handleView($this->view($data));
}
Assuming you are using Symfony version 3.x, you can use the JMS #Expose and #Accessor annotations to control how your entity property is serialized into response data. More info here. However, consider that JMS is bypassed if all you want is to pass your entity to the Twig template rendering engine.
The Doctrine float column type is the correct type to use when handling larger sized floating point numbers such as yours. Your issue is more related to the scientific notation that PHP is designed to use in order to represent these large floats. Allowing arithmetic such as 2.1E-10 + 2.2E-9, but also requiring extra steps to convert their notation to a perhaps more human friendly readable form by using functions such as sprintf and number_format.

Symfony/Doctrine: Why my integer Entity attribute is returning as string?

I have an Entity with an attribute defined as following:
/**
* #var integer
*
* #ORM\Column(name="weight", type="integer")
*/
private $weight;
I was trying to solve a bug and used var_dump() to have some clue of what was happening...
The response was:
string '20' (length=2)
I don't understand why $weight is returning as string... shouldn't it be an integer?
int 20
Or should I handle it in my business logic?
Edit (how I called var_dump()):
I have a class named "Calculator" that iterates $items and use the $weight attribute. Is something like this:
Controller:
$calculator->calculate($category->getItems());
Calculator:
foreach($items as $item) {
//logic...
var_dump($item->getWeight());
}
Edit (database column schema):
Here is the field in database:
weight int(11)
What version of PHP and MySQL driver are you using ?
I don't know exactly how Symfony casts (or not) the database results, but if you're using PHP 5.2, it can be quite normal : any variable you get from MySQL in PHP is always a string, even if it's an integer in the database.
In PHP 5.3, it's different, if you use the correct driver (mysqlnd — See http://blog.ulf-wendel.de/2008/pdo_mysqlnd-the-new-features-of-pdo_mysql/)
See this answer for more details :
https://stackoverflow.com/a/2430657/1741150
Hope that helps !

PHP: How to document array when they are method parameters [duplicate]

This question already has answers here:
PHPDoc for variable-length arrays of arguments
(7 answers)
Closed 5 years ago.
What's the best way to document the elements of an array when it is a parameter to a method? For example, using the PHPDoc headers, I might have something like:
#param array $data
What this doesn't tell me is what elements are mandatory in the array, and what are optional elements. I imagine this should go in the explanation of the method. Something like:
array: $data
============
int $id Required
name $string Required
town $string Optional
/**
* #param array $data
*
* #var $data[id] int, required
* #var $data[name] string, required
* #var $data[town] string, required
*/
This example of using doctrine and zf2 example:
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
* #Form\Attributes({"type":"hidden"})
*/
protected $id;
/**
* #ORM\Column(type="string")
* #Form\Type("Zend\Form\Element\Text")
* #Form\Required({"required":"true"})
* #Form\Filter({"name":"StripTags"})
* #Form\Filter({"name":"StringTrim"})
* #Form\Validator({"name":"StringLength", "options":{"min":"5"}})
* #Form\Options({"label":"First name"})
*/
protected $firstName;
If you have such a complex array with constraints for every single member, I wouldn't use an anonymous array but rather a well defined object. With an array, you can never be sure what it holds, that's somewhat like passing "Object" in e.g. Java, which you rarely would consider a good choice.
However, there is the possibility of a little hinting, when you array contains objects of a certain type as explained here, but that's not a really good answer to your question.
If you really need the parameter as an array, you might document it the way you proposed in the method's description; however, if you use an object as parameter, you'd have additional support in modern IDEs (IntelliSense and so on).
EDIT: I mean, for me the question would be "why would I want to use an anonymous array instead of a self defined type" - and besides simplicity (which will backfire as technical debt later if you maintain and extend your code), I cannot think of a reason, especially compared to what you gain in using a user defined type (self documented code, constraints visible and made explicit by standard methods and so on).
If you just need a dump of data, you might want to go with a simple array, but since you're already thinking about optional and required keys, that screams for a user defined type.
EDIT2: Regarding your comment about if your already have an array as source: I'm not sure whether you need to pass it on as an array or do "mapping" operations as soon as you receive the array (e.g. as $_POST or as return value from some third party library or PHP internal functions or such).
I suppose one could argue that it's not the model's business to interpret data generated by views (e.g. HTML forms which POST data), but rather the controller's resonsibility to react accordingly to the input and transfer the model in the appropriate state. What I mean by this is that you could do something like this if you receive e.g. an array as $_POST:
$customer = new Customer();
$customer->setId($_POST['id']);
$customer->setName($_POST['name']);
$customer->setTown($_POST['town']);
And handle errors as soon as you access the $customer, e.g. throwing exceptions if the name is not set (i.e. $_POST['name'] was empty or such). This way, you use the source array to call setters on the object instead of e.g. passing the array to a factory like Customer::buildByHttpPostData(array $data) and thereby delegating the knowledge of the view's details (names of HTML input tags and such).
Bottom line is, there's no "standard" way to declare required or optional array keys, and of course you can describe those constraints in the method description, but perhaps you can circumvent this by keeping with supported ways like PHPDoc comments on setters or getters.
Of course, there may be better ways to approach the problem, and perhaps somebody comes up with a better answer how to handle it.
To answer the question there is no formal way, try to use a way that you think is most intuitive. I do something similar:
/**
* #param array $data [ int $id, string $name, string $town ]
*/
However I wouldn't use this notation for parameters but rather as return values. In your case I would extract the method arguments to an object and pass that into the method instead:
/**
* #param User $user
*/
public function myMethod( User $user )
{
//...
}
The reason for this is the User object exposes it's properties as an API to other developers, self documenting code!
The other method would be to separate out the array elements into arguments like so:
/**
* #param int $id
* #param string $name
* #param string $town
*/
public function myMethod( $id, $name, $town )
{
//...
}
3 arguments is just about passable but you should start looking for a way to refactor it, like my first suggestion. 4 arguments is generally agreed to be messy and you refactor it.

Is it allowed to set return to json in documentation?

So, while documenting a php code I'm writing, I stopped where I usually said #return string The json output, on functions that I actually returned json.
So, I was wondering if it is right to set
*
* #return json
*/
public function test()
{
$test = array('hola' => array('en' => 'hello', 'ro' => 'salut'));
return json_encode($test);
}
instead of
*
* #return string
*/
public function test()
{
$test = array('hola' => array('en' => 'hello', 'ro' => 'salut'));
return json_encode($test);
}
I searched for related question, and overlooked manuals, but non that I have seen, had mentioned my doubt.
http://manual.phpdoc.org/HTMLSmartyConverter/HandS/phpDocumentor/tutorial_tags.return.pkg.html
Update
Just as a reference, of where I got all this started. I saw a couple of times, the following:
*
* #return View
*/
So, I guess that is a proper return?
As orangepill commented, you should use the type string and add JSON in the description.
#return string JSON
PHPDoc Manual
#return datatype description
#return datatype1|datatype2 description
In reference to the datatype, the manual states
The datatype should be a valid PHP type (int, string, bool, etc), a
class name for the type of object returned, or simply "mixed". If you
want to explicitly show multiple possible return types, list them
pipe-delimited without spaces (e.g. "#return int|string")
"json" is not a primitive type in PHP, or in fact any type. You need to document returned types, not what the content of these types mean. If you specify json as return "type", this implies an object of class json, because json has no other meaning in PHP.
You can only document it as returning a string.
I would prefer to see a return type of json. It's true that you are returning json in the form of a string, however json is more specific, and lets others know what to expect.

Proper way to denote array data type for phpDocumentor?

Which of the following is the proper way to document the return type of this method for phpDocumentor?
Method 1:
/**
* #return array Foo array.
*/
public function foo() {
return array(1, 2, 3);
}
Method 2:
/**
* #return integer[] Foo array.
*/
public function foo() {
return array(1, 2, 3);
}
Also, are there any IDE implications from either method?
Edit:
It appears that both PhpStorm and Netbeans 7.1+ IDEs support the 2nd method.
Both methods are technically correct, but this one is considered 'better' because it's more specific (int and integer are interchangeable):
#return int[]
Documented here:
http://www.phpdoc.org/docs/latest/guides/types.html
At the moment of writing this answer, these are the accepted ways of phpDocumentor (and probably other PHPDoc implementations) to denote an array:
unspecified, no definition of the contents of the represented array is
given. Example: #return array
specified containing a single type, the Type definition informs the
reader of the type of each array element. Only one Type is then
expected as element for a given array.
Example: #return int[]
Please note that mixed is also a single type and with this keyword it
is possible to indicate that each array element contains any possible
type.
specified containing multiple types, the Type definition informs the
reader of the type of each array element. Each element can be of any
of the given types. Example: #return (int|string)[]

Categories