Skip to content

Commit 5c676eb

Browse files
authored
Merge pull request #14 from nuxtifyts/feature/additional-attributes
Feature/additional attributes
2 parents 5ba9880 + 4ef75c9 commit 5c676eb

File tree

18 files changed

+803
-139
lines changed

18 files changed

+803
-139
lines changed

clover.xml

Lines changed: 242 additions & 138 deletions
Large diffs are not rendered by default.

docs/NameMapper.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
Name Mapper
2+
=
3+
4+
Sometimes we could be expecting payload with different letter case or different naming convention.
5+
In such cases, we can use the `NameMapper` attribute to map the property to the correct key in the data array.
6+
7+
```php
8+
use Nuxtifyts\PhpDto\Data;
9+
use Nuxtifyts\PhpDto\Attributes\Class\MapName;
10+
use Nuxtifyts\PhpDto\Enums\LetterCase;
11+
12+
#[MapName(from: [LetterCase::KEBAB, LetterCase::SNAKE])]
13+
final readonly class UserData extends Data
14+
{
15+
public function __construct(
16+
public string $firstName,
17+
public string $lastName
18+
) {}
19+
}
20+
```
21+
22+
In the above example, passed data with keys `letter_case` and `letter-case` will be mapped to `letterCase` (By default),
23+
and all of these keys will be transformed to the selected letter case.
24+
25+
```php
26+
$user = UserData::from([ 'first-name' => 'John', 'last_name': 'Doe' ]);
27+
```
28+
29+
> **Note:** The `MapName` attribute is applied on every key in the data array.
30+
31+
`MapName` attribute accepts these params:
32+
33+
| Param | Type | Description | Default |
34+
|--------|----------------------------------|----------------------------------|-------------------|
35+
| `from` | `LetterCase`\|`list<LetterCase>` | List of letter cases to map from | - |
36+
| `to` | `LetterCase` | Letter case to map to | LetterCase::CAMEL |

docs/PropertyAttributes.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Property Attributes
33

44
In order to provide more functionality to your DTOs, you can use the following attributes:
55
- [Computed](#Computed) - To define a property that is computed from other properties.
6+
- [Hidden](#Hidden) - To define a property that should not be serialized.
67
- [Aliases](#Aliases) - To define aliases for a property.
78
- [DefaultsTo](#DefaultsTo) - To define a default value for a property using a fallback resolver.
89
- [CipherTarget](#CipherTarget) - To define a property that should be encrypted/decrypted.
@@ -33,6 +34,29 @@ final readonly class PersonData extends Data
3334

3435
This will make the DTO aware of the `fullName` property, and it will not be serialized or deserialized.
3536

37+
Hidden
38+
-
39+
40+
Sometimes, we may need to specify that a property should not be serialized.
41+
42+
This can be done using the `Hidden` attribute.
43+
44+
```php
45+
use Nuxtifyts\PhpDto\Attributes\Property\Hidden;
46+
use Nuxtifyts\PhpDto\Data;
47+
48+
final readonly class PersonData extends Data
49+
{
50+
public function __construct(
51+
public string $firstName,
52+
#[Hidden]
53+
public string $lastName
54+
) {}
55+
}
56+
```
57+
58+
When serializing the DTO, the `lastName` property will not be included in the output.
59+
3660
Aliases
3761
-
3862

@@ -56,6 +80,11 @@ final readonly class PersonData extends Data
5680

5781
This will make it possible to hydrate properties from multiple array keys.
5882

83+
> **Note:** Sometimes, we may want to apply the `Aliases` attribute to the whole class,
84+
> in case we want to transform letter cases of all the keys in data array.
85+
> In such cases, we can use the [MapName](https://github.com/nuxtifyts/php-dto/blob/main/docs/NameMapper.md)
86+
> attribute.
87+
5988
CipherTarget
6089
-
6190

docs/Quickstart.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ can be found here:
7878
- [Supported Types](https://github.com/nuxtifyts/php-dto/blob/main/docs/SupportedTypes.md)
7979
- [Normalizers](https://github.com/nuxtifyts/php-dto/blob/main/docs/Normalizers.md)
8080
- [Property Attributes](https://github.com/nuxtifyts/php-dto/blob/main/docs/PropertyAttributes.md)
81+
- [Name Mapper](https://github.com/nuxtifyts/php-dto/blob/main/docs/NameMapper.md)
8182
- [Data Refiners](https://github.com/nuxtifyts/php-dto/blob/main/docs/DataRefiners.md)
8283
- [Empty Data](https://github.com/nuxtifyts/php-dto/blob/main/docs/EmptyData.md)
8384
- [Cloneable Data](https://github.com/nuxtifyts/php-dto/blob/main/docs/CloneableData.md)

src/Attributes/Class/MapName.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Nuxtifyts\PhpDto\Attributes\Class;
4+
5+
use Attribute;
6+
use Nuxtifyts\PhpDto\Enums\LetterCase;
7+
8+
#[Attribute(Attribute::TARGET_CLASS)]
9+
class MapName
10+
{
11+
/**
12+
* @param LetterCase|list<LetterCase> $from
13+
*/
14+
public function __construct(
15+
protected(set) LetterCase|array $from,
16+
protected(set) LetterCase $to = LetterCase::CAMEL
17+
) {
18+
}
19+
}

src/Attributes/Property/Hidden.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Nuxtifyts\PhpDto\Attributes\Property;
4+
5+
use Attribute;
6+
7+
#[Attribute(Attribute::TARGET_PROPERTY)]
8+
class Hidden
9+
{
10+
}

src/Concerns/BaseData.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ final public function jsonSerialize(): array
130130

131131
$serializedData = [];
132132
foreach ($context->properties as $propertyContext) {
133-
if ($propertyContext->isComputed) {
133+
if ($propertyContext->isComputed || $propertyContext->isHidden) {
134134
continue;
135135
}
136136

src/Contexts/ClassContext.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace Nuxtifyts\PhpDto\Contexts;
44

5+
use Nuxtifyts\PhpDto\Attributes\Class\MapName;
56
use Nuxtifyts\PhpDto\Attributes\Class\WithNormalizer;
7+
use Nuxtifyts\PhpDto\Contexts\ClassContext\NameMapperConfig;
68
use Nuxtifyts\PhpDto\Data;
79
use Nuxtifyts\PhpDto\Exceptions\DataCreationException;
810
use Nuxtifyts\PhpDto\Exceptions\UnsupportedTypeException;
@@ -36,6 +38,8 @@ class ClassContext
3638
/** @var array<array-key, class-string<Normalizer>> */
3739
private(set) array $normalizers = [];
3840

41+
private(set) ?NameMapperConfig $nameMapperConfig = null;
42+
3943
/**
4044
* @param ReflectionClass<T> $reflection
4145
*
@@ -118,6 +122,16 @@ private function syncClassAttributes(): void
118122
...$withNormalizerAttribute->newInstance()->classStrings
119123
]);
120124
}
125+
126+
if ($nameMapperAttribute = $this->reflection->getAttributes(MapName::class)[0] ?? null) {
127+
/** @var ReflectionAttribute<MapName> $nameMapperAttribute */
128+
$instance = $nameMapperAttribute->newInstance();
129+
130+
$this->nameMapperConfig = new NameMapperConfig(
131+
from: $instance->from,
132+
to: $instance->to
133+
);
134+
}
121135
}
122136

123137
/**
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Nuxtifyts\PhpDto\Contexts\ClassContext;
4+
5+
use Nuxtifyts\PhpDto\Enums\LetterCase;
6+
use Nuxtifyts\PhpDto\Support\Str;
7+
8+
readonly class NameMapperConfig
9+
{
10+
/** @var list<LetterCase> */
11+
protected array $from;
12+
13+
/**
14+
* @param LetterCase|list<LetterCase> $from
15+
*/
16+
public function __construct(
17+
LetterCase|array $from,
18+
protected LetterCase $to
19+
) {
20+
$this->from = is_array($from) ? $from : [$from];
21+
}
22+
23+
public function transform(string $value): string|false
24+
{
25+
if (Str::validateLetterCase($value, $this->to)) {
26+
return $value;
27+
}
28+
29+
foreach ($this->from as $letterCase) {
30+
if (Str::validateLetterCase($value, $letterCase)) {
31+
return Str::transformLetterCase($value, $letterCase, $this->to);
32+
}
33+
}
34+
35+
return false;
36+
}
37+
}

src/Contexts/PropertyContext.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Nuxtifyts\PhpDto\Attributes\Property\CipherTarget;
1010
use Nuxtifyts\PhpDto\Attributes\Property\Computed;
1111
use Nuxtifyts\PhpDto\Attributes\Property\DefaultsTo;
12+
use Nuxtifyts\PhpDto\Attributes\Property\Hidden;
1213
use Nuxtifyts\PhpDto\Attributes\Property\WithRefiner;
1314
use Nuxtifyts\PhpDto\Contexts\Concerns\HasTypes;
1415
use Nuxtifyts\PhpDto\Data;
@@ -51,6 +52,8 @@ class PropertyContext
5152

5253
private(set) bool $isComputed = false;
5354

55+
private(set) bool $isHidden = false;
56+
5457
private(set) ?CipherConfig $cipherConfig = null;
5558

5659
private(set) ?FallbackConfig $fallbackConfig = null;
@@ -104,6 +107,7 @@ private static function getKey(ReflectionProperty $property): string
104107
private function syncPropertyAttributes(): void
105108
{
106109
$this->isComputed = !empty($this->reflection->getAttributes(Computed::class));
110+
$this->isHidden = !empty($this->reflection->getAttributes(Hidden::class));
107111

108112
foreach ($this->reflection->getAttributes(WithRefiner::class) as $withRefinerAttribute) {
109113
/** @var ReflectionAttribute<WithRefiner> $withRefinerAttribute */

0 commit comments

Comments
 (0)