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

ignore phpstorm attributes when instantiating and add readonly property #281

Merged
merged 21 commits into from
Dec 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a4d3b81
add dependabot configuration
patinthehat Nov 25, 2022
6b67094
add workflow to auto-merge dependabot PRs
patinthehat Nov 25, 2022
63d9d65
Merge pull request #269 from patinthehat/add-dependabot-automation-1
freekmurze Nov 25, 2022
9bf602f
Bump ramsey/composer-install from 1 to 2
dependabot[bot] Nov 25, 2022
b2fc0fd
Bump actions/checkout from 2 to 3
dependabot[bot] Nov 25, 2022
4645fbe
Merge pull request #270 from spatie/dependabot/github_actions/ramsey/…
rubenvanassche Nov 25, 2022
67510af
Merge pull request #271 from spatie/dependabot/github_actions/actions…
rubenvanassche Nov 25, 2022
f452e8d
add missing `array $context` to cast method
goellner Nov 29, 2022
3d7d449
Merge pull request #276 from goellner/patch-1
rubenvanassche Nov 29, 2022
5d53680
Cast date times with a timezone
Dec 7, 2022
c1a83d0
Fixed copy-paste issue
denjaland Dec 7, 2022
617185d
Merge pull request #288 from denjaland/patch-1
rubenvanassche Dec 7, 2022
0fe88c2
Remove request default pipeline
rubenvanassche Dec 7, 2022
16f716b
Fix styling
rubenvanassche Dec 7, 2022
f71c022
Fixed copy-paste issue
denjaland Dec 7, 2022
aeeb1e1
Remove request default pipeline
rubenvanassche Dec 7, 2022
49e6cc7
Fix styling
rubenvanassche Dec 7, 2022
edaff7e
docs update
rubenvanassche Dec 7, 2022
ea80298
Merge branch 'sebdesign-cast-time-zone'
rubenvanassche Dec 7, 2022
1d005c7
Update CHANGELOG
rubenvanassche Dec 7, 2022
689703a
ignore phpstorm attributes when instantiating and add readonly property
jhavens-rcn Dec 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
version: 2

updates:

- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
32 changes: 32 additions & 0 deletions .github/workflows/dependabot-auto-merge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: dependabot-auto-merge
on: pull_request_target

permissions:
pull-requests: write
contents: write

jobs:
dependabot:
runs-on: ubuntu-latest
if: ${{ github.actor == 'dependabot[bot]' }}
steps:

- name: Dependabot metadata
id: metadata
uses: dependabot/[email protected]
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"

- name: Auto-merge Dependabot PRs for semver-minor updates
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}}
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}

- name: Auto-merge Dependabot PRs for semver-patch updates
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
run: gh pr merge --auto --merge "$PR_URL"
env:
PR_URL: ${{github.event.pull_request.html_url}}
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
2 changes: 1 addition & 1 deletion .github/workflows/php-cs-fixer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: ${{ github.head_ref }}

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/phpstan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
name: phpstan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Setup PHP
uses: shivammathur/setup-php@v2
Expand All @@ -25,7 +25,7 @@ jobs:
coverage: none

- name: Install composer dependencies
uses: ramsey/composer-install@v1
uses: ramsey/composer-install@v2

- name: Run PHPStan
run: ./vendor/bin/phpstan --error-format=github
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3

- name: Setup PHP
uses: shivammathur/setup-php@v2
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update-changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
with:
ref: main

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes to `laravel-data` will be documented in this file.

## 2.1.0 - 2022-12-07

- Stop using custom pipeline when creating data magically from requests
- set timezones in datetime casts (#287 )

## 2.0.16 - 2022-11-18

- add support for optional properties in TypeScript transformer (#153)
Expand Down
13 changes: 13 additions & 0 deletions docs/advanced-usage/working-with-dates.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ Now when casting a date, a valid format will be searched. When none can be found

When a transformers hasn't explicitly stated it's format, the first format within the array is used.

## Casting dates in a different time zone

Sometimes a date can be in a different timezone than the timezone you application uses. For example, if your application uses `Europe/Brussels` but your date is in `UTC`:

```php
#[WithCast(DateTimeInterfaceCast::class, timeZone: 'UTC')]
public DateTime $date
```

The date will be created with the `UTC` timezone but will be the same as in the `Europe/Brussels` timezone.

## Changing time zones

When casting a date you may want to set an alternative timezone this can be achieved as such:
Expand All @@ -65,6 +76,8 @@ When casting a date you may want to set an alternative timezone this can be achi
public DateTime $date
```

In this case the time will be transformed, if our original time was in `UTC` then one or two hours (depending on summer time) will be added.

You can also change the timezone of a property which is getting transformed:

```php
Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ That's because the status property expects a `PostStatus` enum object, but it ge
```php
class PostStatusCast implements Cast
{
public function cast(DataProperty $property, mixed $value): PostStatus
public function cast(DataProperty $property, mixed $value, array $context): PostStatus
{
return PostStatus::from($value);
}
Expand Down
2 changes: 1 addition & 1 deletion docs/questions-issues.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ title: Questions and issues
weight: 5
---

Find yourself stuck using the package? Found a bug? Do you have general questions or suggestions for improving Laravel Event Sourcing? Feel free to [create an issue on GitHub](https://github.com/spatie/laravel-data/issues), we'll try to address it as soon as possible.
Find yourself stuck using the package? Found a bug? Do you have general questions or suggestions for improving Laravel Data? Feel free to [create an issue on GitHub](https://github.com/spatie/laravel-data/issues), we'll try to address it as soon as possible.

If you've found a bug regarding security please mail [[email protected]](mailto:[email protected]) instead of using the issue tracker.
9 changes: 7 additions & 2 deletions src/Casts/DateTimeInterfaceCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ class DateTimeInterfaceCast implements Cast
public function __construct(
protected null|string|array $format = null,
protected ?string $type = null,
protected ?string $setTimeZone = null
protected ?string $setTimeZone = null,
protected ?string $timeZone = null
) {
}

Expand All @@ -28,7 +29,11 @@ public function cast(DataProperty $property, mixed $value, array $context): Date

/** @var DateTimeInterface|null $datetime */
$datetime = $formats
->map(fn (string $format) => rescue(fn () => $type::createFromFormat($format, $value), report: false))
->map(fn (string $format) => rescue(fn () => $type::createFromFormat(
$format,
$value,
isset($this->timeZone) ? new DateTimeZone($this->timeZone) : null
), report: false))
->first(fn ($value) => (bool) $value);

if (! $datetime) {
Expand Down
5 changes: 4 additions & 1 deletion src/Resolvers/DataFromArrayResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ public function execute(string $class, Collection $properties): BaseData
$dataClass
->properties
->filter(
fn (DataProperty $property) => ! $property->isPromoted && $properties->has($property->name)
fn (DataProperty $property) =>
! $property->isPromoted &&
! $property->isReadonly &&
$properties->has($property->name)
)
->each(function (DataProperty $property) use ($properties, $data) {
$data->{$property->name} = $properties->get($property->name);
Expand Down
9 changes: 1 addition & 8 deletions src/Resolvers/DataFromSomethingResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Spatie\LaravelData\Contracts\BaseData;
use Spatie\LaravelData\DataPipeline;
use Spatie\LaravelData\DataPipes\AuthorizedDataPipe;
use Spatie\LaravelData\DataPipes\MapPropertiesDataPipe;
use Spatie\LaravelData\DataPipes\ValidatePropertiesDataPipe;
use Spatie\LaravelData\Normalizers\ArrayableNormalizer;
use Spatie\LaravelData\Support\DataConfig;
use Spatie\LaravelData\Support\DataMethod;
Expand Down Expand Up @@ -92,12 +88,9 @@ protected function createFromCustomCreationMethod(string $class, array $payloads

foreach ($payloads as $payload) {
if ($payload instanceof Request) {
DataPipeline::create()
$class::pipeline()
->normalizer(ArrayableNormalizer::class)
->into($class)
->through(AuthorizedDataPipe::class)
->through(MapPropertiesDataPipe::class)
->through(ValidatePropertiesDataPipe::class)
->using($payload)
->execute();
}
Expand Down
9 changes: 6 additions & 3 deletions src/Support/DataClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Spatie\LaravelData\Support;

use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;
Expand Down Expand Up @@ -31,6 +32,7 @@ public function __construct(
public readonly Collection $properties,
public readonly Collection $methods,
public readonly ?DataMethod $constructorMethod,
public readonly bool $isReadonly,
public readonly bool $appendable,
public readonly bool $includeable,
public readonly bool $responsable,
Expand All @@ -43,9 +45,9 @@ public function __construct(

public static function create(ReflectionClass $class): self
{
$attributes = collect($class->getAttributes())->map(
fn (ReflectionAttribute $reflectionAttribute) => $reflectionAttribute->newInstance()
);
$attributes = collect($class->getAttributes())
->filter(fn (ReflectionAttribute $reflectionAttribute) => class_exists($reflectionAttribute->getName()))
->map(fn (ReflectionAttribute $reflectionAttribute) => $reflectionAttribute->newInstance());

$methods = collect($class->getMethods());

Expand All @@ -62,6 +64,7 @@ public static function create(ReflectionClass $class): self
properties: $properties,
methods: self::resolveMethods($class),
constructorMethod: DataMethod::createConstructor($constructor, $properties),
isReadonly: method_exists($class, 'isReadOnly') && $class->isReadOnly(),
appendable: $class->implementsInterface(AppendableData::class),
includeable: $class->implementsInterface(IncludeableData::class),
responsable: $class->implementsInterface(ResponsableData::class),
Expand Down
9 changes: 6 additions & 3 deletions src/Support/DataProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Spatie\LaravelData\Support;

use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use ReflectionAttribute;
use ReflectionProperty;
use Spatie\LaravelData\Attributes\WithCast;
Expand All @@ -24,6 +25,7 @@ public function __construct(
public readonly DataType $type,
public readonly bool $validate,
public readonly bool $isPromoted,
public readonly bool $isReadonly,
public readonly bool $hasDefaultValue,
public readonly mixed $defaultValue,
public readonly ?Cast $cast,
Expand All @@ -41,9 +43,9 @@ public static function create(
?NameMapper $classInputNameMapper = null,
?NameMapper $classOutputNameMapper = null,
): self {
$attributes = collect($property->getAttributes())->map(
fn (ReflectionAttribute $reflectionAttribute) => $reflectionAttribute->newInstance()
);
$attributes = collect($property->getAttributes())
->filter(fn (ReflectionAttribute $reflectionAttribute) => class_exists($reflectionAttribute->getName()))
->map(fn (ReflectionAttribute $reflectionAttribute) => $reflectionAttribute->newInstance());

$mappers = NameMappersResolver::create()->execute($attributes);

Expand All @@ -65,6 +67,7 @@ className: $property->class,
type: DataType::create($property),
validate: ! $attributes->contains(fn (object $attribute) => $attribute instanceof WithoutValidation),
isPromoted: $property->isPromoted(),
isReadonly: $property->isReadOnly(),
hasDefaultValue: $property->isPromoted() ? $hasDefaultValue : $property->hasDefaultValue(),
defaultValue: $property->isPromoted() ? $defaultValue : $property->getDefaultValue(),
cast: $attributes->first(fn (object $attribute) => $attribute instanceof WithCast)?->get(),
Expand Down
102 changes: 74 additions & 28 deletions tests/Casts/DateTimeInterfaceCastTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,35 +98,81 @@
public DateTimeImmutable $dateTimeImmutable;
};

expect(
$caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbon')),
'19-05-1994 00:00:00',
[]
)->getTimezone()
)->toEqual(CarbonTimeZone::create('Europe/Brussels'));
expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbon')),
'19-05-1994 00:00:00',
[]
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbonImmutable')),
'19-05-1994 00:00:00',
[]
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTime')),
'19-05-1994 00:00:00',
[]
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTimeImmutable')),
'19-05-1994 00:00:00',
[]
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));
});

expect(
$caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbonImmutable')),
'19-05-1994 00:00:00',
[]
)->getTimezone()
)->toEqual(CarbonTimeZone::create('Europe/Brussels'));
it('can cast date times with a timezone', function () {
$caster = new DateTimeInterfaceCast('d-m-Y H:i:s', timeZone: 'Europe/Brussels');

expect(
$caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTime')),
'19-05-1994 00:00:00',
[]
)->getTimezone()
)->toEqual(new DateTimeZone('Europe/Brussels'));
$class = new class () {
public Carbon $carbon;

expect(
$caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTimeImmutable')),
'19-05-1994 00:00:00',
[]
)->getTimezone()
)->toEqual(new DateTimeZone('Europe/Brussels'));
public CarbonImmutable $carbonImmutable;

public DateTime $dateTime;

public DateTimeImmutable $dateTimeImmutable;
};

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbon')),
'19-05-1994 00:00:00',
[]
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbonImmutable')),
'19-05-1994 00:00:00',
[]
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTime')),
'19-05-1994 00:00:00',
[]
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTimeImmutable')),
'19-05-1994 00:00:00',
[]
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));
});
Loading