-
Notifications
You must be signed in to change notification settings - Fork 11.3k
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
[5.8] Allow Carbon 2 and custom date handling #25320
Conversation
4df8c59
to
c731b24
Compare
Note: the carbon 2 test pass locally but is skipped remotely because Carbon 2 (as a beta right now) does not match neither |
@@ -8,6 +8,7 @@ | |||
trait InteractsWithTime | |||
{ | |||
/** | |||
* Get the number of seconds until the given DateTime. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you added this by mistake...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @mputkowski! Will fix that.
04f1b55
to
67f4c7b
Compare
+1 Carbon 2 has important improvement because it provides toJson(), a standard way of transfer date in JSON. But it is still in beta. |
It will be in beta until an agreement in principle for Laravel integration, so if some little adjustment is needed to ease the integration I can still add it before I publish the stable release. |
} | ||
} | ||
|
||
if (static::$interceptor) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I use ::use()
, am I not effectively already "intercepting" the stuff and thus this feature is already covered and not needed?
I understand technically it works differently but when I provide ::use()
, I'm effectively already "intercepting" stuff, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it just provide 2 ways to the user, one is easier but limited use
, while intercept
allow you to run any code to convert your instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO there should be only one clear way; again KISS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It must be simple for the user in priority. Date::use(DateTime::class)
is simpler than Date::intercept(function ($date) { return new DateTime($date->format('Y-m-d H:i:s.u'), $date->getTimezone()); });
but Date::use
is not enough for custom conversions (change settings, creating the object in an other way). So an $interceptor
and a $className
, we can convert $className
into $interceptor
inside the use
method, but it's just moving the code to an other place.
So what solution do you propose that would be as easy for the user we it comes to simple class swap and flexible enough for custom conversions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Date::swap(new MyOwnDateClass);
should be enough. If I want any interception, I can do in my class and the facade should not fallback to Carbon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would create a unique date object for the whole application. How could it possibly work?
Code simplified according to @mfn suggestion to use only one method. Now all can be done with the @GrahamCampbell An opinion on this? |
2f822e3
to
ef681e2
Compare
Hi @GrahamCampbell, your requested changes has been made. Any news on this? |
Can you explain the __callStatic method of the Date facade a bit? It looks a little complicated. Also what is the |
So The
If when |
@taylorotwell No problem, comments added. |
if (is_callable($root)) { | ||
// Then we create the date with Carbon then we pass it to this callable that can modify or replace | ||
// This date object with any other value | ||
return $root(Carbon::$method(...$args)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feature [callable block] appears to be quite inconsistent with Laravel facades. Calling swap
should replace the object/class responsible for handling that facade. This is an interceptor / adapter taking action by interpreting a special type of swap, which wasn't a real swap.
Perhaps if people want to intercept their date objects, they should wrap it in their own object / facade.
Real time facades are pretty good at that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@deleugpn This provide a way far easier to convert date objects rather than creating an other class with again a __callStatic method to handle all possible static creations and swap to this class. I'm not in favor of a pure deletion of this.
try { | ||
$root = static::getFacadeRoot(); | ||
} catch (ReflectionException $exception) { | ||
// We mute reflection exceptions as if have Carbon as fallback if the facade root is missing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following every other facade process, there would be one CarbonServiceProvider responsible for binding the accessor into the container, then we would never have a ReflectionException here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@deleugpn Good idea. I'm just wondering the impact of the provider dependency. First with this approach, if you use only some packages (illuminate/database only for example), then you have to run the swap
manually to get dates properly created.
Then, right now, the service is optional (only handle default locale changes) and people may have chosen to disable the auto-discovery. These users will also have to call the swap
before internal dates start to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you only use a specific illuminate package, Facades are not suppose to work anyway because it's the Foundation package that bootstrap them.
You cannot disable auto discovery for service providers inside the framework. What I meant was setting this up in a Service Provider inside the framework.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's not true, facades works with only illuminate/support dependency, and the same dependency is needed for Carbon. But I see what you mean and will think of it.
Taylor's idea of comments was great for me to better see my problem with this. Since the beginning I see this Date Facade deviating too much from the usual Laravel Facades. I guess if we were to split this up into a wrapper class (non-facade) and provide a facade for the wrapper, this would look much more like any other Laravel component. |
Are you sure? I didn't test myself (and: I'm a user of illuminate/database standalone so this important for me) but @kylekatarnls gave me assurance, see #25320 (comment) . |
@deleugpn is wrong on this particular point "that makes facades unavailable". Facades are part of the illuminate/support, illuminate/database depends on it. So if you call swap on a facade then the facade can be used. The Date class proposed here allows not to call swap (as it fallback to Carbon). @mfn I gave the code I used with no problem, you can test it yourself. As you can @deleugpn. I see your point about 2 classes to get a facade that looks more likely other ones as long as it can be done keeping easy tools for Laravel users configuration. I encourage you to test illuminate/support as stand-alone, then swap and run facade. I think the solution should also allow this usage. |
@mfn Yes, I'm sure. The code that @kylekatarnls prepared only works with Eloquent Models (which have their own __callStatic implementation) and the "Date Facade" that he provided here (which would give ReflectionException if he didn't silenced that). Illuminate/Support only provides the Facade classes that relies on a container accessor. For that container accessor to be available, it needs to be bootstrapped and registered by the Service Providers. This is done in the Foundation Package. Laravel Zero (by Nuno Maduro) is a great example of how to use Facades outside of Laravel. You have to mimic the work of the Foundation package for that. This "Date Facade" only works outside of Laravel because it's handling the situation where the Facades have not been bound into the container. This is why I think it should be split into 2 classes: a Wrapper and a Facade. The Wrapper is the thing that would be spread throughout the framework code itself. The facade is just a facilitator for Laravel users. The Wrapper could, then, provide a |
OK for this method. I will try to find some naming that could be more clear for the user about what he get rather than naming that explains how it works which is not relevant for the user when he want just to customize the date class name to use. |
@deleugpn My understanding of the following code: static::$resolvedInstance[static::getFacadeAccessor()] = $instance;
if (isset(static::$app)) {
static::$app->instance(static::getFacadeAccessor(), $instance);
} Is that And it's not Eloquent-related. The So I will still rely on the following system yet provided by Laravel Facades: use the app as facades container but fallback to the simple static array storage if missing. |
f4229c9
to
cf55a12
Compare
According to the @deleugpn idea, I split the facade into 2 classes. A real facade with no try-catch anymore and no __callStatic override. Just the default class fallback if not set in the And a DateFactory that can handle callable, class name or factory as date handler. We still can discuss the naming. But here I think we found a good middle-ground between complexity and ease of use. |
🎉 |
@kylekatarnls, a bit confused by 2 classes - how do I completely replace Carbon with the standard DateTime? Don't want Carbon handling any dates and don't want to ever fallback to it. |
First of all, pure datetime objects can be retrieve from any Carbon instance using Then, in theory: Date::swap(\DateTime::class); Would replace But Laravel (and many libraries you may have installed) expect to have a So depending on you usage it actually may work. But allowing The purpose of Date::swap(new \Carbon\Factory([
'toJsonFormat' => function ($date) {
return $date->toDateTime();
},
])); So I would recommend to rely on inheritance using function purifyDate(\DateTimeInterface $date): \DateTimeInterface
{
if ($date instance \Carbon\CarbonInterface) {
return $date->toDateTime();
}
return $date;
} |
@forestlanelabs I may be wrong here, I'm answering from my phone and only from memory. In theory, you can use Laravel's facade to swap the implementation to a brand new class of your choosing, which should probably adhere to php native DateTime object/interface. However, I think Laravel makes the conscious choice of binding itself to Carbon with Eloquent, so you'll have to work around date time features such as |
Laravel 5.8 introduced a feature to support a custom date class via `Date::use()`, see laravel/framework#25320 When e.g. using `Date::use(CarbonImmutable)` in a project, it means all date casts are not returning `\Illuminate\Support\Carbon` anymore but `\Carbon\CarbonImmutable`, which means all the generated type hints for dates are now wrong. This change tries to be still backwards compatible with Laravel < 5.8 which do not have the Date facade.
Laravel 5.8 introduced a feature to support a custom date class via `Date::use()`, see laravel/framework#25320 When e.g. using `Date::use(CarbonImmutable)` in a project, it means all date casts are not returning `\Illuminate\Support\Carbon` anymore but `\Carbon\CarbonImmutable`, which means all the generated type hints for dates are now wrong. This change tries to be still backwards compatible with Laravel < 5.8 which do not have the Date facade.
Laravel 5.8 introduced a feature to support a custom date class via `Date::use()`, see laravel/framework#25320 When e.g. using `Date::use(CarbonImmutable)` in a project, it means all date casts are not returning `\Illuminate\Support\Carbon` anymore but `\Carbon\CarbonImmutable`, which means all the generated type hints for dates are now wrong. This change tries to be still backwards compatible with Laravel < 5.8 which do not have the Date facade.
Laravel 5.8 introduced a feature to support a custom date class via `Date::use()`, see laravel/framework#25320 When e.g. using `Date::use(CarbonImmutable)` in a project, it means all date casts are not returning `\Illuminate\Support\Carbon` anymore but `\Carbon\CarbonImmutable`, which means all the generated type hints for dates are now wrong. This change tries to be still backwards compatible with Laravel < 5.8 which do not have the Date facade.
Laravel 5.8 introduced a feature to support a custom date class via `Date::use()`, see laravel/framework#25320 When e.g. using `Date::use(CarbonImmutable)` in a project, it means all date casts are not returning `\Illuminate\Support\Carbon` anymore but `\Carbon\CarbonImmutable`, which means all the generated type hints for dates are now wrong. This change tries to be still backwards compatible with Laravel < 5.8 which do not have the Date facade.
Laravel 5.8 introduced a feature to support a custom date class via `Date::use()`, see laravel/framework#25320 When e.g. using `Date::use(CarbonImmutable)` in a project, it means all date casts are not returning `\Illuminate\Support\Carbon` anymore but `\Carbon\CarbonImmutable`, which means all the generated type hints for dates are now wrong. This change tries to be still backwards compatible with Laravel < 5.8 which do not have the Date facade.
Laravel 5.8 introduced a feature to support a custom date class via `Date::use()`, see laravel/framework#25320 When e.g. using `Date::use(CarbonImmutable)` in a project, it means all date casts are not returning `\Illuminate\Support\Carbon` anymore but `\Carbon\CarbonImmutable`, which means all the generated type hints for dates are now wrong. This change tries to be still backwards compatible with Laravel < 5.8 which do not have the Date facade.
This PR allow both Carbon
^1.26.3
and^2.0
to work in Carbon and it allows the user to choose the class he want for dates he receive (such asCarbonImmutable
,Chronos
or simplyDateTime
) and/or intercept date generation to customize the date objects.This PR refactor #25310 using facade instead of factory.
Some examples:
Note: optionally, the
Date
facade could be added to default config app.aliases.