skip to Main Content

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().

2

Answers


  1. Chosen as BEST ANSWER

    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.


  2. 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 IlluminateHttpResourcesJsonJsonResource;
     
    class AppServiceProvider extends ServiceProvider
    {
        public function boot()
        {
            JsonResource::withoutWrapping();
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search