I have an API endpoint that points to /api/invoices and it returns a list of invoices.
Some invoices return something like this:
{
"id": 2555,
"entity_id": 88,
"net_total": 7.5,
"total_vat": 1.725000000000000088817841970012523233890533447265625,
"grand_total": 9.2249999999999996447286321199499070644378662109375,
}
As you can see, there's too many decimal places. My money columns are all defined as double(20,5), because the software needs to handle up to 5 decimal places (but mostly is only 2) and 20 units.
Is there any way to force either through MySQL or Laravel, to always return 5 decimal places? I can't use:
->selectRaw('ROUND(total_vat, 5) as total_vat')
Because I have around 100 columns and I get them all without using ->select().
I would suggest using API Resources for transforming data.
If you just return Invoices::all() in your API, then yes, it will return "as is".
But a typical way to transform any data would be API Resources:
php artisan make:resource InvoiceResource
Then, inside of that resource, you return only what you need and transformed however you want, including the 5 digits:
app/Http/Resources/InvoiceResource.php:
public function toArray($request)
{
return [
'id' => $this->id,
'entity_id' => $this->entity_id,
'net_total' => $this->net_total,
'total_vat' => number_format($this->total_vat, 5),
'grand_total' => number_format($this->grand_total, 5),
];
}
Then, in Controller:
public function index() {
return InvoiceResource::collection(Invoice::all());
}
Also, API Resources would add "data" wrapper layer by default when returned, so you may want to avoid that, then add this to AppServiceProvider:
app/Providers/AppServiceProvider.php:
use Illuminate\Http\Resources\Json\JsonResource;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
JsonResource::withoutWrapping();
}
}
The solution is to convert the columns into decimals and maintain the same structure, like said in this post https://stackoverflow.com/a/24135191/3355243.
I did a few tests and the column value kept the original data.
However, this will lead to another issue, which is laravel casting decimal as string. By using postman, we can see that currency values comes as string, like "15.00". To solve this we can use cast Eloquent casts decimal as string:
protected $casts =
[
'net_total' => 'float',
];
I wasn't able to find / check if using the cast creates any problem with the original value.
Be careful while converting DOUBLE to DECIMAL, as, like in my case, this may trigger severe problems in your project. For example, my mobile App stopped working after the conversion, because is not ready to handle the currency values as string.
Related
I have a method in the controller that calls a service, specifically this method that returns quotes for activities. I'm unsure about the correctness of the logic. Is it correct to pass the parameters as UUIDs to the service and then convert them internally to obtain the internal ID? Internally, I work with IDs, while publicly, I expose UUIDs.
Thank you!
public function getQuotes(string $businessUUID, string $businessTypeUUID, array $filters): Collection
{
// Get the internal id from the uuid
$filters['business_id'] = $this->businessService->getBusinessByUUID($businessUUID)->id;
$filters['business_type_id'] = $this->businessTypeService->getBusinessTypeByUUID($businessTypeUUID)->id;
// Retrieve the quotes that match the given filters
$quotes = BusinessQuote::query()->withFilters($filters)->get();
// If no quotes match the given filters
if ($quotes->isEmpty()) {
throw new ModelNotFoundException(__('Quotes not found'));
}
// Return the quotes
return $quotes;
}
Yes, this approach is correct and safe because a chance to guess UUID is currently impossible. Using integer IDs you get better performance than working with UUID internally.
Keep in mind, is recommended to use UUID v4 for maximum security.
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.
I've recently updated my server to a newer version of MySQL and PHP 7 for various reasons. On my previous instance, running PHP 5.5, Laravel's response()->json() always converted tinyint's into a string. Now running newer server software, it's returning me int's -as it should...
I'd have to change a lots of my codebase to either cast types / convert them into a string manually, whic I'm trying to avoid at the moment.
Is there a way to somehow force response()->json() to return int's as string's?
Is there a way to somehow force response()->json() to return int's as string's
I don't want to change the code base - do not want to cast types, convert it,
No. There's no option for that. You need to do that yourself if needed.
There is a way to cast integer into string in laravel
in your model you can cast id to string. Its as follows
protected $casts = [ 'id' => 'string' ];
But the downside is that you would have to do that for all Models.
If you don't want to modify a lot of code you could run response data through a quick and dirty function. Instead of going directory to JSON you should instead grab the data as a nested array. Then put it through a function like this:
function convertIntToString ($myArray) {
foreach ($myArray as $thisKey => $thisValue) {
if (is_array($thisValue)) {
// recurse to handle a nested array
$myArray[$thisKey] = convertIntToString($thisValue);
} elseif (is_integer($thisValue)) {
// convert any integers to a string
$myArray[$thisKey] = (string) $thisValue;
}
}
return $myArray;
}
The function will convert integers to strings and use recursion to handle nested arrays. Take the output from that and then convert it to JSON.
The best solution for me is to to use attribute casting and
Fractal transformers
Fractal transformers are extremely useful when you have complex responses with multiple relations included.
You can typecast it to string:
return response->json(["data" => (string)1]);
Is it possible to do this within the $project array using PHP's built in round function?
I try to enclose my output value within the round function to 2 decimal places:
"Energy" => round(array('$multiply' => array("$energy", 10)), 2),
The output error I get is this:
Type: MongoDB\Driver\Exception\RuntimeException
Code: 16406
Message: The top-level _id field is the only field currently supported for exclusion
File: C:\wamp\www\DRM\vendor\mongodb\mongodb\src\Operation\Aggregate.php
Line: 168
Currently I have a separate parsing method which takes care of all the rounding, but what I'd like is to do it within the aggregate function in PHP.
Is this possible? I know MongoDB doesn't have round, but there is an external library for that.
there is no runding capabilities for mongo yet.
as per this answer, you could add extra steps to aggregation pipeline to get it rounded - below mongo shell code:
> db.a.save({x:1.23456789})
> db.a.save({x:9.87654321})
> db.a.aggregate([{$project:{ _id:0,
y:{$divide:[
{$subtract:[
{$multiply:['$x',100]},
{$mod:[{$multiply:['$x',100]}, 1]}
]},
100]}
}}])
{ "y" : 1.23 }
{ "y" : 9.87 }
jira ticket
In the middle of a period of big refactoring at work, I wish to introduce stdClass ***** as a way to return data from functions and I'm trying to find non-subjective arguments to support my decision.
Are there any situations when would it be best to use one instead of the other ??
What benefits would I get to use stdClass instead of arrays ??
Some would say that functions have to be as little and specific to be able to return one single value. My decision to use stdClass is temporal, as I hope to find the right Value Objects for each process on the long run.
The usual approach is
Use objects when returning a defined data structure with fixed branches:
$person
-> name = "John"
-> surname = "Miller"
-> address = "123 Fake St"
Use arrays when returning a list:
"John Miller"
"Peter Miller"
"Josh Swanson"
"Harry Miller"
Use an array of objects when returning a list of structured information:
$person[0]
-> name = "John"
-> surname = "Miller"
-> address = "123 Fake St"
$person[1]
-> name = "Peter"
-> surname = "Miller"
-> address = "345 High St"
Objects are not suitable to hold lists of data, because you always need a key to address them. Arrays can fulfill both functions - hold arbitrary lists, and a data structure.
Therefore, you can use associative arrays over objects for the first and third examples if you want to. I'd say that's really just a question of style and preference.
#Deceze makes a number of good points on when to use an object (Validation, type checking and future methods).
Using stdClass to fulfill the same function as an array is not very useful IMHO, it just adds the overhead of an object without any real benefit. You're also missing out on many useful array functions (e.g. array_intersect). You should at least create your own class to enable type checking, or add methods to the object to make it worth using an object.
I don't think there is any reasonable advantage of using a stdClass over an array as long as your sole intention is to return multiple arbitrary datatypes from a function call.
Since you cannot technically return multiple values natively, you have to use a container that can hold all other datatypes available in PHP. That would be either an object or an array.
function fn1() { return array(1,2); }
function fn2() { return array('one' => 1, 'two' => 2); }
function fn3() { return (object) array(1,2); }
function fn4() { return (object) array('one' => 1, 'two' => 2); }
All of the above would work. The array is a tiny negligible fraction faster and less work to type. It also has a clearly defined purpose in contrast to the generic stdClass (which is a bit wishywashy, isnt it). Both only have an implicit interface, so you will have to look at the docs or the function body to know what they will contain.
If you want to use objects at any cost, you could use ArrayObject or SplFixedArray, but if you look at their APIs would you say you need their functionality for the simple task of returning random multiple values? I don't think so. Don't get me wrong though: if you want to use stdClass, then use it. It's not like it would break anything. But you also would not gain anything. To add at least some benefit, you could create a separate class named ReturnValues for this.
Could be a simple tagging class
class ReturnValues {}
or something more functional
class ReturnValues implements Countable
{
protected $values;
public function __construct() { $this->values = func_get_args(); }
public function __get($key) return $this->values[$key]; }
public function count() { return count($this->values); }
}
Granted, it doesn't do much and getting the values out of it is still done through an implict interface, but at least the class has a more clearly defined responsibility now. You could extend from this class to create ReturnValue objects for particular operations and give those an explicit interface:
class FooReturnValues extends ReturnValues
{
public function getFoo() { return $this->values['foo']; }
public function getBar() { return $this->values['foo']; }
}
Now the developer just has to look at the API to know which multiple values foo() will return. Of course, having to write concrete ReturnValue classes for each and every operation that might return multiple values could become tedious quickly. And personally, I find this overengineered for the initial purpose.
Anyway, hope that makes any sense.
Well, there are 3 differences:
they have an identity. which is why the default for passing array arguments is call by value and for objects call by sharing.
there is a semantical difference. If you use an object, anyone who reads the code understand, that the value represents the model some sort of entitity, while an array is supposed to act as a collection or a map
And last but not least, refactoring becomes signifficantly easier. If you want to use a concrete class rather than stdClass, all you have to do is to instantiate another class. Which also allows you to add methods.
greetz
back2dos
The only OBJECTIVE vote of confidence I can find is:
json_decode uses stdClass by default so us mortals in userland should use stdClass for similar situations.
I find stdClass objects over arrays useful when I need to keep my code clean and somewhat sentence-like readable. Take for example function getProperties() which returns a set of properties, say data about a person (name, age, sex). If getProperties() would return an associative array, when you want to use one of the returned properties, you would write two instructions:
$data = getProperties();
echo $data['name'];
On the other hand, if getProperties() returns a stdClass then you could write that in just one instruction:
echo getProperties()->name;
In tests of array vs stdclass they way php handles dynamic properties is slower then associative arrays. I'm not saying this to argue micro optimization but rather if your going to do this your better off defining a dataclass with no methods and set public properties. Esp if you are using php 5.4+. Under the hood defined properties are mapped directly to a c array with no hashtable where as dynamic ones need to use a hash table.
This has the added bonus of later becoming a full class without any major interface reworking.