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

[10.x] Add a Number utility class #48845

Merged
merged 21 commits into from
Nov 15, 2023

Conversation

caendesilva
Copy link
Contributor

@caendesilva caendesilva commented Oct 28, 2023

Abstract

Adds a new Number utility class, in the Illuminate\Support namespace, as suggested by Taylor in #48827 (comment).

use Illuminate\Support\Number;

Number::format(123456789) // 123,456,789
Number::toCurrency(59.99) // $59.99
Number::bytesToHuman(1536) // 1.5 KB
Number::toHuman(10000) // ten thousand

PR Development

The class ports the bytesToHuman() helper from #48827, and after discussion here and in #internals adds some wrappers for the NumberFormatter class. As this requires the php-intl extension, I added a check for those wrappers that throws a RuntimeException. I also added the extension as a suggestion to the Support package composer.json.

Please let me know if this route works, otherwise I'm happy to make changes.

If anyone has ideas on more helpers, please let me know!

Future development

If this gets merged, I'll also make a PR that adds a Numberable (or something better named), similar to Stringable, that allows Number::of(100)->toHuman() and other neat stuff like that.

@caendesilva caendesilva changed the title [10.x] Add a helperNumber utility class [10.x] Add a Number utility class Oct 28, 2023
@caendesilva caendesilva marked this pull request as ready for review October 29, 2023 10:11
@caendesilva
Copy link
Contributor Author

I'm marking this ready for review, as I personally have no more ideas on what utilities to add. But if anyone has ideas I am more than happy to implement!

@henzeb
Copy link
Contributor

henzeb commented Oct 29, 2023

I'm marking this ready for review, as I personally have no more ideas on what utilities to add. But if anyone has ideas I am more than happy to implement!

https://book.cakephp.org/2/en/core-libraries/helpers/number.html

@realodix
Copy link

realodix commented Oct 29, 2023

I'm marking this ready for review, as I personally have no more ideas on what utilities to add. But if anyone has ideas I am more than happy to implement!

as suggested by Taylor in #48827 (comment)

number_to_currency, number_to_human, number_to_human_size, number_to_percentage, number_with_delimiter, number_with_precision

@caendesilva
Copy link
Contributor Author

I'm marking this ready for review, as I personally have no more ideas on what utilities to add. But if anyone has ideas I am more than happy to implement!

as suggested by Taylor in #48827 (comment)

number_to_currency, number_to_human, number_to_human_size, number_to_percentage, number_with_delimiter, number_with_precision

Gonna take a stab at a Number::toHuman() helper now!

@caendesilva caendesilva marked this pull request as draft October 29, 2023 18:46
Comment on lines 12 to 25
/**
* Format the number to a fluent human-readable string.
*
* @param float|int $number
* @param string $locale
* @return false|string
*/
public static function toHuman($number, $locale = 'en')
{
$formatter = new NumberFormatter($locale, NumberFormatter::SPELLOUT);

return $formatter->format($number);
}

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 is unfortunately dependent on the ext-intl, as it wraps the NumberFormatter class. Assuming this is acceptable, we may want to add a check for the extension in the NumberFormatter class, and throw an exception if it is not available. But I understand if this is not worth it. However, given how much code a DIY solution would require, I personally think it is worth it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If this is acceptable, I'll go right ahead and add some more wrappers as the NumberFormatter class has a lot of useful helpers.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@taylorotwell Hey! Hope it's okay to ping you. Just wanted your opinion regarding wrapping the NumberFormatter class, and how we should handle that.

This is unfortunately dependent on the `ext-intl`, as it wraps the NumberFormatter class.
@marcovo
Copy link
Contributor

marcovo commented Oct 30, 2023

The use of prefixes in bytesToHuman() seems to follow a custom naming scheme instead of established standards.

I notice two things:

  1. The prefixes used seem to be metrix prefixes (k, M, G, ...), calculating base-10, with the exception that an uppercase K is used instead of the official lowercase k
  2. The calculations seem to be done using base-2, which would actually suggest binary prefixes (Ki, Mi, Gi, ...) should be used

I propose to replace bytesToHuman() with two other methods:

  1. bytesToMetric(), using division by 1000 and using metric abbreviations kB, MB, GB, ...
  2. bytesToBinary() (or some better name), using division by 1024 and using binary abbreviations KiB, MiB, GiB, ...

Other suggestions: toMetricBytes/toBinaryBytes, formatMetricBytes/formatBinaryBytes

@caendesilva
Copy link
Contributor Author

The use of prefixes in bytesToHuman() seems to follow a custom naming scheme instead of established standards.

I notice two things:

  1. The prefixes used seem to be metrix prefixes (k, M, G, ...), calculating base-10, with the exception that an uppercase K is used instead of the official lowercase k
  2. The calculations seem to be done using base-2, which would actually suggest binary prefixes (Ki, Mi, Gi, ...) should be used

I propose to replace bytesToHuman() with two other methods:

  1. bytesToMetric(), using division by 1000 and using metric abbreviations kB, MB, GB, ...
  2. bytesToBinary() (or some better name), using division by 1024 and using binary abbreviations KiB, MiB, GiB, ...

Thank you for the feedback! You are correct that the kB should be lowercase. I will fix that. As for the rest, you are also correct that I am technically using binary abbreviations, as that's what computers use. I realise this makes the method quite opinionated, so let's await some extra feedback on that matter and see what others think.

@marcovo
Copy link
Contributor

marcovo commented Oct 31, 2023

I want to stress that the code in the current state uses binary calculations in combination with decimal/metric abbreviations, a combination that would lead to technically confusing results. Do note however, that Windows (up to this date) also does exactly that: it reports filesizes in multiples of 1024 while using metric abbreviations. I would propose to not follow this false windows-path and stick to binary calculations with binary prefixes and/or metric calculations with metric prefixes.

As discussed in #internals, this seems to be a good compromise when the extension is not used. Instead of testing against the exception in the tests, I just skipped the test if the extension is missing, as that is what we do in the InteractsWithRedis testing trait.
@caendesilva
Copy link
Contributor Author

I want to stress that the code in the current state uses binary calculations in combination with decimal/metric abbreviations, a combination that would lead to technically confusing results. Do note however, that Windows (up to this date) also does exactly that: it reports filesizes in multiples of 1024 while using metric abbreviations. I would propose to not follow this false windows-path and stick to binary calculations with binary prefixes and/or metric calculations with metric prefixes.

Noted. I'm considering if we should add an option for if to use base 10 or base 2 but I'm weary of adding a bunch of complexity. Ideally I would like to get input from the core team on what they think is best.

@@ -32,7 +32,7 @@ public static function toHuman($number, $locale = 'en')
*/
public static function bytesToHuman($bytes, $precision = 2)
{
$units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
$units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
Copy link
Contributor Author

@caendesilva caendesilva Nov 1, 2023

Choose a reason for hiding this comment

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

There has been some discussion whether we should use kB or KB.

It's worth noting that Rails uses KB. https://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html

@khalyomede
Copy link
Contributor

Hi, congratulations on this PR, looks very useful I already can see some home made helpers I could replace by this one.

One question comes to my mind, will the toHuman() method will take into account the locale? I mean this:

$number = Number::of(10);

$number->toCurrency("EUR"); // "€10.00"

App::setLocale("fr");

$number->toCurrency("EUR"); // "10,00 €" 

@caendesilva
Copy link
Contributor Author

caendesilva commented Nov 4, 2023

Hi, congratulations on this PR, looks very useful I already can see some home made helpers I could replace by this one.

One question comes to my mind, will the toHuman() method will take into account the locale? I mean this:

$number = Number::of(10);

$number->toCurrency("EUR"); // "€10.00"

App::setLocale("fr");

$number->toCurrency("EUR"); // "10,00 €" 

Glad you like it! Yes, someone on Twitter pointed this out, so I am just about to implement this.

Edit: See 9cbebdd

@tresorkalanda-chd
Copy link

I'd also like it to have a helper that allows you to do this without importing the Number class directly.

number()->toHuman();

This makes so that if no locale parameter is specified, the configured App locale is used.
@taylorotwell taylorotwell merged commit 6e4ecc7 into laravel:10.x Nov 15, 2023
20 checks passed
@taylorotwell
Copy link
Member

Thanks 👍

taylorotwell added a commit to illuminate/support that referenced this pull request Nov 15, 2023
* Create a new Number utility class

* Add a `Number::bytesToHuman()` helper

Ports laravel/framework#48827 into the new `Number` utility class

* Add a `Number::toHuman()` helper

This is unfortunately dependent on the `ext-intl`, as it wraps the NumberFormatter class.

* Use lowercase `k` for kilobytes

See laravel/framework#48845 (comment)

* Update Support package to suggest `ext-intl`

* Throw if extension is not installed when using NumberFormatter wrapper

As discussed in #internals, this seems to be a good compromise when the extension is not used. Instead of testing against the exception in the tests, I just skipped the test if the extension is missing, as that is what we do in the InteractsWithRedis testing trait.

* Add a `Number::toCurrency()` helper

* Make Number helper locale parameters null and default to App locale

This makes so that if no locale parameter is specified, the configured App locale is used.

* Add a `Number::format()` helper

Adds a locale-aware number formatting helper

* Fix number tests

Could not get Mockery to work, so this is my fix.

* Add a `Number::toPercent()` helper

I'm dividing the supplied value by 100 so that 50 = 50% as that's how Rails does it, and that's what Taylor linked to in his suggestion for this class. The default number formatter would consider 0.5 to be 50% and 50 to be 5000%. I'm not sure which option is best, so I went with the Rails format. https://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_percentage

* Rename Number toHuman helper to spellout

We may want to remove it, as per laravel/framework#48845 (comment). But renaming it for now.

* Create new `Number::toHuman()` helper

Based on the Rails implementation, as requested by Taylor in laravel/framework#48845 (comment)

See https://api.rubyonrails.org/classes/ActionView/Helpers/NumberHelper.html#method-i-number_to_human

Uses the short scale system, see https://en.wikipedia.org/wiki/Long_and_short_scales

* Change toHuman implementation to better match Rails version

Based more on the logic of Rails, but with added support for massive numbers.

* Update toHuman helper to better handle extreme numbers

Inverts negative numbers, and removes unreachable cases, and handles very large numbers

* Clean up toHuman helper

* formatting

* formatting

* formatting

* formatting

* formatting

---------

Co-authored-by: Taylor Otwell <taylor@laravel.com>
* @param int $precision
* @return string
*/
public static function forHumans(int|float $number, int $precision = 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

What about other languages?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, maybe the __() function could be used to provide translation support?

Copy link
Contributor

Choose a reason for hiding this comment

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

caendesilva added a commit to caendesilva/laravel-framework that referenced this pull request Nov 17, 2023
Building on laravel#48845, this class will work similarly to the `Stringable` class, but for numbers.
caendesilva added a commit to caendesilva/laravel-framework that referenced this pull request Nov 17, 2023
Building on laravel#48845, this class will work similarly to the `Stringable` class, but for numbers.
@rmunate
Copy link
Contributor

rmunate commented Nov 18, 2023

@caendesilva @taylorotwell Hello Engineers, I would like to take into account the following comment, I would like to add a functionality that is called Number::spell() and that this returns an instance of my package "https://github.com/rmunate/SpellNumber" to translate to letters, to currency format and to ordinals.

I would like to be able to contemplate it, it would be something like this

Number::spell(100)->locale('en')->toLetters();
// "One Hundred"

Number::spell(100)->locale('es')->toLetters();
// "Cien"

Number::spell(100)->locale('fa')->toLetters();
// "صد"

Number::spell(100)->locale('hi')->toLetters();
// "एक सौ"

Number::spell((123456789.12)->locale('en')->toLetters();
// "One Hundred Twenty-Three Million Four Hundred Fifty-Six Thousand Seven Hundred Eighty-Nine And Twelve"

Number::spell(2)->locale('en')->toOrdinal();
// "Second"

Number::spell(2)->locale('en')->toOrdinal(SpellNumber::ORDINAL_DEFAULT);
// "Second"

Number::spell(2)->locale('es')->toOrdinal(SpellNumber::ORDINAL_MALE);
// "Segundo"

Number::spell(2)->locale('es')->toOrdinal(SpellNumber::ORDINAL_FEMALE);
// "Segunda"

//etc

See docs : https://rmunate.github.io/SpellNumber/

what do you think? Do you want me to work on adjusting this?

I could commit to refining the package to seamlessly integrate with the solution you've implemented. Furthermore, it doesn't solely cater to English; we are enhancing its internationalization capabilities. Additionally, the handling of ordinals varies across languages, and this aspect has already been addressed.

@OzanKurt @utsavsomaiya @tresorkalanda-chd @khalyomede @marcovo @realodix @henzeb

@rmunate
Copy link
Contributor

rmunate commented Nov 18, 2023

I think the human implementation is a bit better in Rails. This one where it totally spells out the word doesn't feel immediately useful.

#48845 (comment)

@caendesilva
Copy link
Contributor Author

@caendesilva @taylorotwell Hello Engineers, I would like to take into account the following comment, I would like to add a functionality that is called Number::spell() and that this returns an instance of my package "https://github.com/rmunate/SpellNumber" to translate to letters, to currency format and to ordinals.

I'm not a maintainer so take this with a grain of salt, but I think what you are proposing would add too much complexity to be in the core framework, as most people don't need advanced spellout helpers, and those that do can use your package. Remember that this Number utility class is macroable, so your package could be updated to extend this class with your methods.

@andrey-helldar
Copy link
Contributor

It's fine for basic use, but if you need more flexible localization support, it's better to use the kwn/number-to-words package.

@TursunboyevJahongir
Copy link

this function works great, but can we use it with locate ?

Number::toHuman(1000, 'en') //1 thousand
Number::toHuman(1000, 'fr') //1 mille

@jtibbles
Copy link

Hi, great work. However, regarding German formatting:
Number::toCurrency(5.49, currency: 'EUR', locale: 'de') // 5.49

....in Germany (de_de) and Austria (de_at) they use a comma, not a dot. €5.49 is actually displayed as "€5,49".
Naturally it's always stored in a DB table with a dot (unless it's stored as a string) and converted to a dot for calculations.
Is there any chance this can be amended in a future update?

@caendesilva
Copy link
Contributor Author

this function works great, but can we use it with locate ?

Number::toHuman(1000, 'en') //1 thousand
Number::toHuman(1000, 'fr') //1 mille

I like the idea and am gonna play around with it when I have some time over!

@caendesilva
Copy link
Contributor Author

Hi, great work. However, regarding German formatting: Number::toCurrency(5.49, currency: 'EUR', locale: 'de') // 5.49

....in Germany (de_de) and Austria (de_at) they use a comma, not a dot. €5.49 is actually displayed as "€5,49". Naturally it's always stored in a DB table with a dot (unless it's stored as a string) and converted to a dot for calculations. Is there any chance this can be amended in a future update?

Are you able to specify de_de/de_at as the locale? If those are incorrect, then this would be an bug in the upstream PHP extension.

@jtibbles
Copy link

jtibbles commented Nov 22, 2023

You can specify those and others that use the same format (cs_CZ for example). It's possible in the php.ini (setlocale(LC_MONETARY, 'cs_CZ') ). But many of my projects now expect multiple language packs now so while the amount and currency symbol stays the same, the formatting needs to be swapped out on the fly. Changing the locale for this is a bit cumbersome. If toCurrency can handle it instead it takes some weight off.
There's always str_replace I guess

@caendesilva
Copy link
Contributor Author

You can specify those and others that use the same format (cs_CZ for example). It's possible in the php.ini (setlocale(LC_MONETARY, 'cs_CZ') ). But many of my projects now expect multiple language packs now so while the amount and currency symbol stays the same, the formatting needs to be swapped out on the fly. Changing the locale for this is a bit cumbersome. If toCurrency can handle it instead it takes some weight off. There's always str_replace I guess

You should probably be able to add a service provider or middleware to automatically set the Number::useLocale() to the request user's locale.

caendesilva added a commit to caendesilva/laravel-framework that referenced this pull request Nov 23, 2023
## Abstract

This has been requested by a large number of people after laravel#48845. My attempt at implementing localization support for this method is using translation helpers.

## Example

```php
// Example language setup

'sv' => [
    'thousand' => 'tusen',
    'million' => 'miljon',
    'billion' => 'miljard',
    'trillion' => 'biljon',
    'quadrillion' => 'biljard',
],
```
caendesilva added a commit to caendesilva/laravel-framework that referenced this pull request Nov 23, 2023
This has been requested by a large number of people after laravel#48845. My attempt at implementing localization support for this method is using translation helpers.

```php
// Example language setup

'sv' => [
    'thousand' => 'tusen',
    'million' => 'miljon',
    'billion' => 'miljard',
    'trillion' => 'biljon',
    'quadrillion' => 'biljard',
],
```
renovate bot referenced this pull request in RadioRoster/backend Nov 24, 2023
[![Mend Renovate logo
banner](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [laravel/framework](https://laravel.com)
([source](https://github.com/laravel/framework)) | `10.30.1` ->
`10.33.0` |
[![age](https://developer.mend.io/api/mc/badges/age/packagist/laravel%2fframework/10.33.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/packagist/laravel%2fframework/10.33.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/packagist/laravel%2fframework/10.30.1/10.33.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/packagist/laravel%2fframework/10.30.1/10.33.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|

---

### Release Notes

<details>
<summary>laravel/framework (laravel/framework)</summary>

###
[`v10.33.0`](https://github.com/laravel/framework/blob/HEAD/CHANGELOG.md#v10330---2023-11-21)

[Compare
Source](https://github.com/laravel/framework/compare/v10.32.1...v10.33.0)

- \[10.x] Fix wrong parameter passing and add these rules to dependent
rules by [@&#8203;kayw-geek](https://github.com/kayw-geek) in
[https://github.com/laravel/framework/pull/49008](https://github.com/laravel/framework/pull/49008)
- \[10.x] Make Validator::getValue() public by
[@&#8203;shinsenter](https://github.com/shinsenter) in
[https://github.com/laravel/framework/pull/49007](https://github.com/laravel/framework/pull/49007)
- \[10.x] Custom messages for `Password` validation rule by
[@&#8203;rcknr](https://github.com/rcknr) in
[https://github.com/laravel/framework/pull/48928](https://github.com/laravel/framework/pull/48928)
- \[10.x] Round milliseconds in database seeder console output runtime
by [@&#8203;SjorsO](https://github.com/SjorsO) in
[https://github.com/laravel/framework/pull/49014](https://github.com/laravel/framework/pull/49014)
- \[10.x] Add a `Number` utility class by
[@&#8203;caendesilva](https://github.com/caendesilva) in
[https://github.com/laravel/framework/pull/48845](https://github.com/laravel/framework/pull/48845)
- \[10.x] Fix the replace() method in DefaultService class by
[@&#8203;jonagoldman](https://github.com/jonagoldman) in
[https://github.com/laravel/framework/pull/49022](https://github.com/laravel/framework/pull/49022)
- \[10.x] Pass the property $validator as a parameter to the $callback
Closure by [@&#8203;shinsenter](https://github.com/shinsenter) in
[https://github.com/laravel/framework/pull/49015](https://github.com/laravel/framework/pull/49015)
- \[10.x] Fix Cache DatabaseStore::add() error occur on Postgres within
transaction by [@&#8203;xdevor](https://github.com/xdevor) in
[https://github.com/laravel/framework/pull/49025](https://github.com/laravel/framework/pull/49025)
- \[10.x] Support asserting against chained batches by
[@&#8203;taylorotwell](https://github.com/taylorotwell) in
[https://github.com/laravel/framework/pull/49003](https://github.com/laravel/framework/pull/49003)
- \[10.x] Prevent DB `Cache::get()` occur race condition by
[@&#8203;xdevor](https://github.com/xdevor) in
[https://github.com/laravel/framework/pull/49031](https://github.com/laravel/framework/pull/49031)
- \[10.x] Fix notifications being counted as sent without a "shouldSend"
method by [@&#8203;joelwmale](https://github.com/joelwmale) in
[https://github.com/laravel/framework/pull/49030](https://github.com/laravel/framework/pull/49030)
- \[10.x] Fix tests failure on Windows by
[@&#8203;hafezdivandari](https://github.com/hafezdivandari) in
[https://github.com/laravel/framework/pull/49037](https://github.com/laravel/framework/pull/49037)
- \[10.x] Add unless conditional on validation rules by
[@&#8203;michaelnabil230](https://github.com/michaelnabil230) in
[https://github.com/laravel/framework/pull/49048](https://github.com/laravel/framework/pull/49048)
- \[10.x] Handle string based payloads that are not JSON or form data
when creating PSR request instances by
[@&#8203;timacdonald](https://github.com/timacdonald) in
[https://github.com/laravel/framework/pull/49047](https://github.com/laravel/framework/pull/49047)
- \[10.x] Fix directory separator CMD display on windows by
[@&#8203;imanghafoori1](https://github.com/imanghafoori1) in
[https://github.com/laravel/framework/pull/49045](https://github.com/laravel/framework/pull/49045)
- \[10.x] Fix mapSpread doc by
[@&#8203;timacdonald](https://github.com/timacdonald) in
[https://github.com/laravel/framework/pull/48941](https://github.com/laravel/framework/pull/48941)
- \[10.x] Tiny `Support\Collection` test fix - Unused data provider
parameter by [@&#8203;stevebauman](https://github.com/stevebauman) in
[https://github.com/laravel/framework/pull/49053](https://github.com/laravel/framework/pull/49053)
- \[10.x] Feat: Add color_hex validation rule by
[@&#8203;nikopeikrishvili](https://github.com/nikopeikrishvili) in
[https://github.com/laravel/framework/pull/49056](https://github.com/laravel/framework/pull/49056)
- \[10.x] Handle missing translation strings using callback by
[@&#8203;DeanWunder](https://github.com/DeanWunder) in
[https://github.com/laravel/framework/pull/49040](https://github.com/laravel/framework/pull/49040)
- \[10.x] Add Str::transliterate to Stringable by
[@&#8203;dwightwatson](https://github.com/dwightwatson) in
[https://github.com/laravel/framework/pull/49065](https://github.com/laravel/framework/pull/49065)
- Add Alpha Channel support to Hex validation rule by
[@&#8203;ahinkle](https://github.com/ahinkle) in
[https://github.com/laravel/framework/pull/49069](https://github.com/laravel/framework/pull/49069)

###
[`v10.32.1`](https://github.com/laravel/framework/blob/HEAD/CHANGELOG.md#v10321---2023-11-14)

[Compare
Source](https://github.com/laravel/framework/compare/v10.32.0...v10.32.1)

- \[10.x] Add `[@pushElseIf](https://github.com/pushElseIf)` and
`[@pushElse](https://github.com/pushElse)` by
[@&#8203;jasonmccreary](https://github.com/jasonmccreary) in
[https://github.com/laravel/framework/pull/48990](https://github.com/laravel/framework/pull/48990)

###
[`v10.32.0`](https://github.com/laravel/framework/blob/HEAD/CHANGELOG.md#v10320---2023-11-14)

[Compare
Source](https://github.com/laravel/framework/compare/v10.31.0...v10.32.0)

- Update PendingRequest.php by
[@&#8203;mattkingshott](https://github.com/mattkingshott) in
[https://github.com/laravel/framework/pull/48939](https://github.com/laravel/framework/pull/48939)
- \[10.x] Change array_key_exists with null coalescing assignment
operator in FilesystemAdapter by
[@&#8203;miladev95](https://github.com/miladev95) in
[https://github.com/laravel/framework/pull/48943](https://github.com/laravel/framework/pull/48943)
- \[10.x] Use container to resolve email validator class by
[@&#8203;orkhanahmadov](https://github.com/orkhanahmadov) in
[https://github.com/laravel/framework/pull/48942](https://github.com/laravel/framework/pull/48942)
- \[10.x] Added `getGlobalMiddleware` method to HTTP Client Factory by
[@&#8203;pascalbaljet](https://github.com/pascalbaljet) in
[https://github.com/laravel/framework/pull/48950](https://github.com/laravel/framework/pull/48950)
- \[10.x] Detect MySQL read-only mode error as a lost connection by
[@&#8203;cosmastech](https://github.com/cosmastech) in
[https://github.com/laravel/framework/pull/48937](https://github.com/laravel/framework/pull/48937)
- \[10.x] Adds more implicit validation rules for `present` based on
other fields by
[@&#8203;diamondobama](https://github.com/diamondobama) in
[https://github.com/laravel/framework/pull/48908](https://github.com/laravel/framework/pull/48908)
- \[10.x] Refactor set_error_handler callback to use arrow function in
`InteractsWithDeprecationHandling` by
[@&#8203;miladev95](https://github.com/miladev95) in
[https://github.com/laravel/framework/pull/48954](https://github.com/laravel/framework/pull/48954)
- \[10.x] Test Improvements by
[@&#8203;crynobone](https://github.com/crynobone) in
[https://github.com/laravel/framework/pull/48962](https://github.com/laravel/framework/pull/48962)
- Fix issue that prevents BladeCompiler to raise an exception when
temporal compiled blade template is not found. by
[@&#8203;juanparati](https://github.com/juanparati) in
[https://github.com/laravel/framework/pull/48957](https://github.com/laravel/framework/pull/48957)
- \[10.x] Fix how nested transaction callbacks are handled by
[@&#8203;mateusjatenee](https://github.com/mateusjatenee) in
[https://github.com/laravel/framework/pull/48859](https://github.com/laravel/framework/pull/48859)
- \[10.x] Fixes Batch Callbacks not triggering if job timeout while in
transaction by [@&#8203;crynobone](https://github.com/crynobone) in
[https://github.com/laravel/framework/pull/48961](https://github.com/laravel/framework/pull/48961)
- \[10.x] expressions in migration computations fail by
[@&#8203;tpetry](https://github.com/tpetry) in
[https://github.com/laravel/framework/pull/48976](https://github.com/laravel/framework/pull/48976)
- \[10.x] Fixes Exception: Cannot traverse an already closed generator
when running Arr::first with an empty generator and no callback by
[@&#8203;moshe-autoleadstar](https://github.com/moshe-autoleadstar) in
[https://github.com/laravel/framework/pull/48979](https://github.com/laravel/framework/pull/48979)
- fixes issue with stderr when there was "]" character. by
[@&#8203;nikopeikrishvili](https://github.com/nikopeikrishvili) in
[https://github.com/laravel/framework/pull/48975](https://github.com/laravel/framework/pull/48975)
- \[10.x] Fix Postgres cache store failed to put exist cache in
transaction by [@&#8203;xdevor](https://github.com/xdevor) in
[https://github.com/laravel/framework/pull/48968](https://github.com/laravel/framework/pull/48968)

###
[`v10.31.0`](https://github.com/laravel/framework/blob/HEAD/CHANGELOG.md#v10310---2023-11-07)

[Compare
Source](https://github.com/laravel/framework/compare/v10.30.1...v10.31.0)

- \[10.x] Allow `Sleep::until()` to be passed a timestamp as a string by
[@&#8203;jameshulse](https://github.com/jameshulse) in
[https://github.com/laravel/framework/pull/48883](https://github.com/laravel/framework/pull/48883)
- \[10.x] Fix whereHasMorph() with nullable morphs by
[@&#8203;MarkKremer](https://github.com/MarkKremer) in
[https://github.com/laravel/framework/pull/48903](https://github.com/laravel/framework/pull/48903)
- \[10.x] Handle `class_parents` returning false in
`class_uses_recursive` by
[@&#8203;RoflCopter24](https://github.com/RoflCopter24) in
[https://github.com/laravel/framework/pull/48902](https://github.com/laravel/framework/pull/48902)
- \[10.x] Enable default retrieval of all fragments in `fragments()` and
`fragmentsIf()` methods by [@&#8203;tabuna](https://github.com/tabuna)
in
[https://github.com/laravel/framework/pull/48894](https://github.com/laravel/framework/pull/48894)
- \[10.x] Allow placing a batch on a chain by
[@&#8203;khepin](https://github.com/khepin) in
[https://github.com/laravel/framework/pull/48633](https://github.com/laravel/framework/pull/48633)
- \[10.x] Dispatch 'connection failed' event in async http client
request by [@&#8203;gdebrauwer](https://github.com/gdebrauwer) in
[https://github.com/laravel/framework/pull/48900](https://github.com/laravel/framework/pull/48900)
- authenticate method refactored to use null coalescing operator by
[@&#8203;miladev95](https://github.com/miladev95) in
[https://github.com/laravel/framework/pull/48917](https://github.com/laravel/framework/pull/48917)
- \[10.x] Add support for Sec-Purpose header by
[@&#8203;nanos](https://github.com/nanos) in
[https://github.com/laravel/framework/pull/48925](https://github.com/laravel/framework/pull/48925)
- \[10.x] Allow setting retain_visibility config option on Flysystem
filesystems by [@&#8203;jnoordsij](https://github.com/jnoordsij) in
[https://github.com/laravel/framework/pull/48935](https://github.com/laravel/framework/pull/48935)
- \[10.x] Escape forward slashes when exploding wildcard rules by
[@&#8203;matt-farrugia](https://github.com/matt-farrugia) in
[https://github.com/laravel/framework/pull/48936](https://github.com/laravel/framework/pull/48936)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/Lapotor/RadioRoster-api).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy41OS44IiwidXBkYXRlZEluVmVyIjoiMzcuNTkuOCIsInRhcmdldEJyYW5jaCI6Im1haW4ifQ==-->

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
@WikiRik
Copy link

WikiRik commented Nov 28, 2023

@taylorotwell it seems that a part of the functionality was lost in one of your formatting commits; 6d3a6c2
Before this commit it used App::getLocale() to retrieve the default locale of the app and now it only has a new method called useLocale. Can you elaborate on why this change was made? Before people open a PR that adds the functionality to use the globally set locale (like was expected by Povilas of Laravel Daily in this video; https://www.youtube.com/watch?v=PRhhqukK9uw )

@caendesilva
Copy link
Contributor Author

@taylorotwell it seems that a part of the functionality was lost in one of your formatting commits; 6d3a6c2 Before this commit it used App::getLocale() to retrieve the default locale of the app and now it only has a new method called useLocale. Can you elaborate on why this change was made? Before people open a PR that adds the functionality to use the globally set locale (like was expected by Povilas of Laravel Daily in this video; https://www.youtube.com/watch?v=PRhhqukK9uw )

Thanks for bringing this up! I've been wondering this too, especially since it's something I've been asked about a bunch.

@andrersilva96
Copy link

Hi @caendesilva I was testing with brazilian currency, I believe that we use comma to separate decimals and dot for thousands:
R$11,000.36 // \Illuminate\Support\Number::currency(11000.36, 'BRL')
R$ 11.000,36 // using my own helper

@caendesilva
Copy link
Contributor Author

Hi @caendesilva I was testing with brazilian currency, I believe that we use comma to separate decimals and dot for thousands: R$11,000.36 // \Illuminate\Support\Number::currency(11000.36, 'BRL') R$ 11.000,36 // using my own helper

Hey, @andrersilva96 thanks for bringing it up! So I took a quick look, and does seem like the Brazilian reals are indeed divided using commas (inverted behaviour compared to the US dollar formatting). Though I did not in my brief search find an official source.

However, this helper is a wrapper for the PHP Intl extension, so this means that the bug is present in the PHP Core and needs to be fixed there. I suggest you file a bug report through here https://www.php.net/manual/en/install.problems.bugs.php. Feel free to ping me in the report!

@becker
Copy link

becker commented Jan 19, 2024

@andrersilva96, I believe you could do this way:

Number::currency(11000.36, 'BRL', 'pt_BR')
// "R$ 11.000,36"

@andrersilva96
Copy link

@becker This worked, thank's a lot ou melhor obrigado meu conterrâneo! haha

@shaedrich
Copy link
Contributor

shaedrich commented Jul 8, 2024

From rmunate/SpellNumber#24 after the repository has been discontinued:

grafik
(see Appendix:English numerals)

+ Scientific notation/engineering notation/E notation

$input = SpellNumber::integer(2);
$input->toLatinateOrdinal(); // "secondary"
$input->toAdverbial(); // "twice" as in "twice as good"
$input->toMultiplier(); // "twofold"
$input->toLatinateMultiplier(); // "double" as in "double the amount"
$input->toDistributive(); // "doubly"
$input->toMetricCollectivePrefix(); // "double-"
$input->toLatinateCollectivePrefix(); // "bi-" as in "bi-weekly"
$input->toFractional(); // "half" as in "half as good"
$input->toMetricFractionalPrefix(); // "demi-" as in "demi-god"
$input->toLatinateFractionalPrefix(); // "semi-" as in "semipermeable"
$input = SpellNumber::value(3200);
$input->toScientificNotation(); // 3.2e3 or 3.2e-3 or 3.2E-3
$input->toEngineeringNotation(); // aliases
$input->toENotation(); // aliases

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.