diff --git a/docs/en/annotation/alisa-annotation.md b/docs/en/annotation/alisa-annotation.md index 7be00ae..cba8605 100644 --- a/docs/en/annotation/alisa-annotation.md +++ b/docs/en/annotation/alisa-annotation.md @@ -128,9 +128,8 @@ $complexUser = ComplexUser::from([ 'user_tags' => ['developer', 'programmer'] ]); -// Convert to standard array -$complexUserArray = $complexUser->toArray(); -// $complexUserArray toArray: +var_dump($complexUserListFaker) +// Content of $complexUserListFaker: // [ // 'profile' => UserProfile Object ([ // 'nickname' => 'job', diff --git a/docs/en/faker/collection-faker.md b/docs/en/faker/collection-faker.md index be9a1f7..4d9269b 100644 --- a/docs/en/faker/collection-faker.md +++ b/docs/en/faker/collection-faker.md @@ -21,8 +21,8 @@ $userList = UserListFaker::faker(); $complexUserListFaker = UserListFaker::faker(); -$complexUserListFakerArray = $complexUserListFaker->toArray(); -// Content of $complexUserListFakerArray: +var_dump($complexUserListFaker) +// Content of $complexUserListFaker: // [ // 'profile' => [ // [0] => UserProfile Object ( diff --git a/docs/en/mapper/array-mapper.md b/docs/en/mapper/array-mapper.md index f836f7f..ff63f73 100644 --- a/docs/en/mapper/array-mapper.md +++ b/docs/en/mapper/array-mapper.md @@ -40,8 +40,8 @@ $data1 = MultiArraySerialize::from( ] ); -$data1Array = $data1->toArray(); -// Content of $data1Array: +var_dump($data1) +// Content of $data1: // [ // 'mixedTypeArray' => [ // [0] => ArrayOne Object @@ -72,8 +72,8 @@ $data2 = MultiArraySerialize::from( ] ); -$data2Array = $data2->toArray(); -// Content of $data2Array: +var_dump($data2) +// Content of $data2: // [ // 'multiTypeArray' => [ // ArrayOne Object ( @@ -97,8 +97,8 @@ $data3 = MultiArraySerialize::from( ] ); -$data3Array = $data3->toArray(); -// $data3Array toArray: +var_dump($data3) +// Content of $data3: // [ // 'keyValueMixedArray' => [ // 'user1' => ArrayOne Object ( @@ -121,8 +121,8 @@ $data4 = MultiArraySerialize::from( ] ); -$data4Array = $data4->toArray(); -// $data4Array toArray: +var_dump($data4) +// Content of $data4: // [ // 'mixedTypeArray' => [ // ['unknown' => 'data1'], diff --git a/docs/en/mapper/union-mapper.md b/docs/en/mapper/union-mapper.md index b9f538c..e4bbfde 100644 --- a/docs/en/mapper/union-mapper.md +++ b/docs/en/mapper/union-mapper.md @@ -60,8 +60,8 @@ $data2 = FlexibleData::from([ echo $data2->userIdentifier; // Output User object echo $data2->complexIdentifier; // Output AdminUser object -$data2Array = $data2->toArray(); -// Content of $data2Array: +var_dump($data2) +// Content of $data2: // [ // 'flexibleId' => 'ABC123', // 'userIdentifier' => User Object ( @@ -87,11 +87,11 @@ $data3 = FlexibleData::from([ ] ]); -echo $data2->userIdentifier; // Output User object -echo $data2->complexIdentifier; // Output AdminUser object +echo $data3->userIdentifier; // Output User object +echo $data3->complexIdentifier; // Output AdminUser object -$data3Array = $data3->toArray(); -// Content of $data3Array: +var_dump($data3) +// Content of $data3: // [ // 'flexibleId' => 'USER001', // 'userIdentifier' => User Object ( diff --git a/docs/zh/annotation/alisa-annotation.md b/docs/zh/annotation/alisa-annotation.md index ff61fb7..77ad700 100644 --- a/docs/zh/annotation/alisa-annotation.md +++ b/docs/zh/annotation/alisa-annotation.md @@ -128,8 +128,7 @@ $complexUser = ComplexUser::from([ 'user_tags' => ['developer', 'programmer'] ]); -// 转换为标准数组 -$complexUserArray = $complexUser->toArray(); +var_dump($complexUser); // $complexUserArray 的内容: // [ // 'profile' => UserProfile Object ([ diff --git a/docs/zh/faker/collection-faker.md b/docs/zh/faker/collection-faker.md index a7b2126..7208ccd 100644 --- a/docs/zh/faker/collection-faker.md +++ b/docs/zh/faker/collection-faker.md @@ -21,8 +21,8 @@ $userList = UserListFaker::faker(); $complexUserListFaker = UserListFaker::faker(); -$complexUserListFakerArray = $complexUserListFaker->toArray(); -// $complexUserListFakerArray 的内容: +var_dump($complexUserListFaker) +// $complexUserListFaker 的内容: // [ // 'profile' => [ // [0] => UserProfile Object ( diff --git a/docs/zh/mapper/array-mapper.md b/docs/zh/mapper/array-mapper.md index 17434cc..5ecc9e3 100644 --- a/docs/zh/mapper/array-mapper.md +++ b/docs/zh/mapper/array-mapper.md @@ -40,8 +40,8 @@ $data1 = MultiArraySerialize::from( ] ); -$data1Array = $data1->toArray(); -// $data1Array 的内容: +var_dump($data1) +// $data1 的内容: // [ // 'mixedTypeArray' => [ // [0] => ArrayOne Object @@ -72,8 +72,8 @@ $data2 = MultiArraySerialize::from( ] ); -$data2Array = $data2->toArray(); -// $data2Array 的内容: +var_dump($data2) +// $data2 的内容: // [ // 'multiTypeArray' => [ // ArrayOne Object ( @@ -97,8 +97,8 @@ $data3 = MultiArraySerialize::from( ] ); -$data3Array = $data3->toArray(); -// $data3Array 的内容: +var_dump($data3) +// $data3 的内容: // [ // 'keyValueMixedArray' => [ // 'user1' => ArrayOne Object ( @@ -121,7 +121,7 @@ $data4 = MultiArraySerialize::from( ] ); -$data4Array = $data4->toArray(); +var_dump($data4) // $data4Array 的内容: // [ // 'mixedTypeArray' => [ diff --git a/docs/zh/mapper/union-mapper.md b/docs/zh/mapper/union-mapper.md index 08d838e..0e38e9b 100644 --- a/docs/zh/mapper/union-mapper.md +++ b/docs/zh/mapper/union-mapper.md @@ -60,7 +60,7 @@ $data2 = FlexibleData::from([ echo $data2->userIdentifier; // 输出 User 对象 echo $data2->complexIdentifier; // 输出 User 对象 -$data2Array = $data2->toArray(); +var_dump($data2) // $data2Array 的内容: // [ // 'flexibleId' => 'ABC123', @@ -87,10 +87,10 @@ $data3 = FlexibleData::from([ ] ]); -echo $data2->userIdentifier; // 输出 User 对象 -echo $data2->complexIdentifier; // 输出 AdminUser 对象 +echo $data3->userIdentifier; // 输出 User 对象 +echo $data3->complexIdentifier; // 输出 AdminUser 对象 -$data3Array = $data3->toArray(); +var_dump($data3) // $data3Array 的内容: // [ // 'flexibleId' => 'USER001', diff --git a/src/Casts/Normalizer/ArrayNormalizerCast.php b/src/Casts/Normalizer/ArrayNormalizerCast.php new file mode 100644 index 0000000..c6527a5 --- /dev/null +++ b/src/Casts/Normalizer/ArrayNormalizerCast.php @@ -0,0 +1,22 @@ +match($values)){ + return $values->toArray(); + } + + return $values; + } +} \ No newline at end of file diff --git a/src/Casts/Normalizer/JsonNormalizerCast.php b/src/Casts/Normalizer/JsonNormalizerCast.php new file mode 100644 index 0000000..1bf6fe0 --- /dev/null +++ b/src/Casts/Normalizer/JsonNormalizerCast.php @@ -0,0 +1,28 @@ +match($values)){ + try { + $decoded = json_decode($values, true, 512, JSON_THROW_ON_ERROR); + return is_array($decoded) ? $decoded : $values; + } catch (JsonException $e) { + + } + } + + return $values; + } +} \ No newline at end of file diff --git a/src/Contracts/Normalizer/NormalizerCastInterface.php b/src/Contracts/Normalizer/NormalizerCastInterface.php new file mode 100644 index 0000000..09d49fa --- /dev/null +++ b/src/Contracts/Normalizer/NormalizerCastInterface.php @@ -0,0 +1,10 @@ +configManager->getNormalizerCasts() as $cast) { + $values = $cast->resolve($values); + } + + return $values; + } +} \ No newline at end of file diff --git a/src/Resolvers/OutputResolver.php b/src/Resolvers/OutputResolver.php index 01a9014..d8df062 100644 --- a/src/Resolvers/OutputResolver.php +++ b/src/Resolvers/OutputResolver.php @@ -3,6 +3,7 @@ namespace Astral\Serialize\Resolvers; use Astral\Serialize\Resolvers\Casts\OutputCastResolver; +use Astral\Serialize\Serialize; use Astral\Serialize\Support\Collections\DataCollection; use Astral\Serialize\Support\Collections\GroupDataCollection; use Astral\Serialize\Support\Context\ChoosePropertyContext; @@ -22,7 +23,6 @@ public function __construct( public function resolve(ChooseSerializeContext $chooseContext, GroupDataCollection $groupCollection, object $object): array { - $context = new OutContext( className: $groupCollection->getClassName(), classInstance: $object, @@ -32,7 +32,6 @@ classInstance: $object, $properties = $groupCollection->getProperties(); - $toArray = []; foreach ($properties as $collection) { @@ -53,8 +52,9 @@ classInstance: $object, ); foreach ($matchData['names'] as $name) { - $toArray[$name] = $resolvedValue; + $toArray[$name] = $resolvedValue instanceof Serialize ? $resolvedValue->toArray() : $resolvedValue; } + } return $toArray; diff --git a/src/Serialize.php b/src/Serialize.php index 04faf3b..11e4f9e 100644 --- a/src/Serialize.php +++ b/src/Serialize.php @@ -7,6 +7,14 @@ use Astral\Serialize\Support\Factories\ContextFactory; use JsonSerializable; +/** + * @method void withResponses(array $responses) static + * @see SerializeContext::withResponses() + * @method void setCode(string|int $code) static + * @see SerializeContext::setCode() + * @method void setMessage(string $message) static + * @see SerializeContext::setMessage() + */ abstract class Serialize implements JsonSerializable { private ?SerializeContext $_context = null; @@ -35,6 +43,16 @@ public function withGroups(array|string $groups): static return $this; } + public function toJsonString(): bool|string + { + return json_encode($this); + } + + public function withoutResponseToJsonString(): string + { + return json_encode($this->toArray()); + } + public function toArray(): array { if ($this->getContext() === null) { @@ -66,6 +84,7 @@ public static function faker(): static return $instance; } + public function __debugInfo() { $res = get_object_vars($this); @@ -76,14 +95,17 @@ public function __debugInfo() public function jsonSerialize(): array { - $baseResponse = Config::get('response',[]); - if($baseResponse){ + $baseResponses = Config::get('response',[]); + $customerResponses = $this->getContext()?->getResponses() ?? []; + $responses = array_merge($baseResponses, $customerResponses); + + if($responses){ $resultData = []; - foreach ($baseResponse as $field => $item){ + foreach ($responses as $field => $item){ if($item === 'T'){ $resultData[$field] = $this->toArray(); }else{ - $resultData[$field] = $item['example'] ?? ''; + $resultData[$field] = $item['value'] ?? ($item['example'] ?? ''); } } return $resultData; @@ -91,4 +113,15 @@ public function jsonSerialize(): array return $this->toArray(); } + + public function __call(string $name, array $arguments) + { + if ($this->getContext() === null) { + $this->setContext(ContextFactory::build(static::class)); + } + + $this->getContext()->{$name}(...$arguments); + + return $this; + } } diff --git a/src/SerializeContainer.php b/src/SerializeContainer.php index a17ddc7..8715a71 100644 --- a/src/SerializeContainer.php +++ b/src/SerializeContainer.php @@ -8,6 +8,7 @@ use Astral\Serialize\Faker\Rule\FakerDefaultRules; use Astral\Serialize\Resolvers\Casts\DataCollectionCastResolver; use Astral\Serialize\Resolvers\Casts\InputValueCastResolver; +use Astral\Serialize\Resolvers\Casts\NormalizerCastResolver; use Astral\Serialize\Resolvers\Casts\OutputCastResolver; use Astral\Serialize\Resolvers\GroupResolver; use Astral\Serialize\Resolvers\InputResolver; @@ -41,6 +42,7 @@ class SerializeContainer protected ?GroupResolver $groupResolver = null; protected ?ReflectionClassInstanceManager $reflectionClassInstanceManager = null; protected ?SerializeInstanceManager $serializeInstanceManager = null; + protected ?NormalizerCastResolver $normalizerCastResolver = null; protected ?DataCollectionCastResolver $attributePropertyResolver = null; protected ?InputResolver $propertyInputValueResolver = null; protected ?OutputResolver $propertyToArrayResolver = null; @@ -119,6 +121,11 @@ public function propertyInputValueResolver(): InputResolver ); } + public function normalizerCastResolver(): NormalizerCastResolver + { + return $this->normalizerCastResolver ??= new NormalizerCastResolver(ConfigManager::getInstance()); + } + public function inputValueCastResolver(): InputValueCastResolver { return $this->inputValueCastResolver ??= new InputValueCastResolver(ConfigManager::getInstance()); diff --git a/src/Support/Config/ConfigManager.php b/src/Support/Config/ConfigManager.php index 90b72b7..20db6a3 100644 --- a/src/Support/Config/ConfigManager.php +++ b/src/Support/Config/ConfigManager.php @@ -7,12 +7,15 @@ use Astral\Serialize\Casts\InputValue\InputObjectBestMatchChildCast; use Astral\Serialize\Casts\InputValue\InputValueEnumCast; use Astral\Serialize\Casts\InputValue\InputValueNullCast; +use Astral\Serialize\Casts\Normalizer\ArrayNormalizerCast; +use Astral\Serialize\Casts\Normalizer\JsonNormalizerCast; use Astral\Serialize\Casts\OutValue\OutArrayChildCast; use Astral\Serialize\Casts\OutValue\OutValueEnumCast; use Astral\Serialize\Casts\OutValue\OutValueGetterCast; use Astral\Serialize\Contracts\Attribute\DataCollectionCastInterface; use Astral\Serialize\Contracts\Attribute\InputValueCastInterface; use Astral\Serialize\Contracts\Attribute\OutValueCastInterface; +use Astral\Serialize\Contracts\Normalizer\NormalizerCastInterface; use Astral\Serialize\Enums\CacheDriverEnum; use Astral\Serialize\Exceptions\NotFoundAttributePropertyResolver; use Astral\Serialize\Support\Caching\MemoryCache; @@ -35,11 +38,17 @@ class ConfigManager /** @var (OutValueCastInterface|string)[] $outputValueCasts */ private array $outputValueCasts = [ - OutArrayChildCast::class, +// OutArrayChildCast::class, OutValueEnumCast::class, OutValueGetterCast::class, ]; + /** @var (NormalizerCastInterface|string)[] $normalizerCasts */ + private array $normalizerCasts = [ +// JsonNormalizerCast::class, + ArrayNormalizerCast::class, + ]; + /** @var CacheDriverEnum|class-string $cacheDriver */ private string|CacheDriverEnum $cacheDriver = MemoryCache::class; @@ -52,6 +61,10 @@ public function __construct() foreach ($this->outputValueCasts as $key => $cast) { $this->outputValueCasts[$key] = new $cast(); } + + foreach ($this->normalizerCasts as $key => $cast) { + $this->normalizerCasts[$key] = new $cast(); + } } public static function getInstance(): ConfigManager @@ -59,6 +72,19 @@ public static function getInstance(): ConfigManager return self::$instance ??= new self(); } + /** + * @throws NotFoundAttributePropertyResolver + */ + public function addNormalizerCasts(NormalizerCastInterface|string $resolverClass): static + { + if (is_string($resolverClass) && !is_subclass_of($resolverClass, NormalizerCastInterface::class)) { + throw new NotFoundAttributePropertyResolver('Resolver class must be an instance of NormalizerCastInterface'); + } + $this->attributePropertyResolver[] = (is_string($resolverClass) ? new $resolverClass() : $resolverClass); + + return $this; + } + /** * @throws NotFoundAttributePropertyResolver */ @@ -113,6 +139,11 @@ public function getOutValueCasts(): array return $this->outputValueCasts; } + public function getNormalizerCasts(): array + { + return $this->normalizerCasts; + } + public function getCacheDriver(): string { if ($this->cacheDriver instanceof CacheDriverEnum) { diff --git a/src/Support/Context/SerializeContext.php b/src/Support/Context/SerializeContext.php index 679a861..b601405 100644 --- a/src/Support/Context/SerializeContext.php +++ b/src/Support/Context/SerializeContext.php @@ -5,6 +5,7 @@ use Astral\Serialize\Exceptions\NotFoundGroupException; use Astral\Serialize\Faker\FakerResolver; use Astral\Serialize\Resolvers\Casts\DataCollectionCastResolver; +use Astral\Serialize\Resolvers\Casts\NormalizerCastResolver; use Astral\Serialize\Resolvers\GroupResolver; use Astral\Serialize\Resolvers\InputResolver; use Astral\Serialize\Resolvers\OutputResolver; @@ -25,6 +26,7 @@ class SerializeContext { private array $groups = []; + private array $responses = []; public function __construct( /** @var class-string */ @@ -38,10 +40,32 @@ public function __construct( private readonly InputResolver $propertyInputValueResolver, private readonly OutputResolver $propertyToArrayResolver, private readonly FakerResolver $fakerResolver, + private readonly NormalizerCastResolver $normalizerCastResolver, ) { } + public function setCode(string|int $code, $description ='' , $field = 'code'): void + { + $this->responses[$field] = ['description' => $description,'value' => $code]; + } + + public function setMessage(string $message, $description ='' , $field = 'message'): void + { + $this->responses[$field] = ['description' => $description,'value' => $message]; + } + + public function withResponses(array $responses): self + { + $this->responses = $responses; + return $this; + } + + public function getResponses(): array + { + return $this->responses ?? []; + } + public function getChooseSerializeContext(): ChooseSerializeContext { return $this->chooseSerializeContext; @@ -189,6 +213,7 @@ public function from(mixed ...$payload): object { $payloads = []; foreach ($payload as $field => $itemPayload) { + $itemPayload = $this->normalizerCastResolver->resolve($itemPayload); $values = is_numeric($field) && is_array($itemPayload) ? $itemPayload : [$field => $itemPayload]; $payloads = [...$payloads, ...$values]; } @@ -220,4 +245,5 @@ public function toArray(object $object): array $this->chooseSerializeContext->setGroups($this->getGroups()); return $this->propertyToArrayResolver->resolve($this->chooseSerializeContext, $this->getGroupCollection(), $object); } + } diff --git a/src/Support/Factories/ContextFactory.php b/src/Support/Factories/ContextFactory.php index 9b20d6a..4661f00 100644 --- a/src/Support/Factories/ContextFactory.php +++ b/src/Support/Factories/ContextFactory.php @@ -28,6 +28,7 @@ public static function build(string $className): SerializeContext propertyInputValueResolver: SerializeContainer::get()->propertyInputValueResolver(), propertyToArrayResolver: SerializeContainer::get()->propertyToArrayResolver(), fakerResolver: SerializeContainer::get()->fakerResolver(), + normalizerCastResolver: SerializeContainer::get()->normalizerCastResolver(), )); } } diff --git a/tests/Serialize/From/NormalizerFromSerializeTest.php b/tests/Serialize/From/NormalizerFromSerializeTest.php new file mode 100644 index 0000000..9feac3a --- /dev/null +++ b/tests/Serialize/From/NormalizerFromSerializeTest.php @@ -0,0 +1,89 @@ +name_one = 'one name'; + $normalizerOne->id_one = 1; + + $normalizerTwo = new NormalizerTwo(); + $normalizerTwo->name_two = 'two name'; + $normalizerTwo->id_two = 2; + + $res = NormalizerClass::from(one: $normalizerOne, two: $normalizerTwo, three: $normalizerOne); + + expect($res->one)->toBeInstanceOf(NormalizerOne::class) + ->and($res->one->name_one)->toBe('one name') + ->and($res->one->id_one)->toBe(1) + ->and($res->two)->toBeInstanceOf(NormalizerTwo::class) + ->and($res->two->name_two)->toBe('two name') + ->and($res->two->id_two)->toBe(2) + ->and($res->three)->toBeArray() + ->and($res->three)->toMatchArray([ + 'name_one' => 'one name', + 'id_one' => 1 + ]); +// + +}); + +it('test json_encode Serialize class', function () { + + $normalizerOne = new NormalizerOne(); + $normalizerOne->name_one = 'one name'; + $normalizerOne->id_one = 1; + + $normalizerTwo = new NormalizerTwo(); + $normalizerTwo->name_two = 'two name'; + $normalizerTwo->id_two = 2; + + $res = NormalizerClass::from(one: $normalizerOne, two: $normalizerTwo, three: $normalizerOne); + + $resJson = json_encode($res); + expect($resJson)->toBe('{"code":200,"message":"\u64cd\u4f5c\u6210\u529f","data":{"one":{"name_one":"one name","id_one":1},"two":{"name_two":"two name","id_2":2},"three":{"name_one":"one name","id_one":1}}}'); + + $res->setMessage('233'); + $resJson = json_encode($res); + expect($resJson)->toBe('{"code":200,"message":"233","data":{"one":{"name_one":"one name","id_one":1},"two":{"name_two":"two name","id_2":2},"three":{"name_one":"one name","id_one":1}}}'); + + $res->setCode(-1); + $resJson = json_encode($res); + expect($resJson)->toBe('{"code":-1,"message":"233","data":{"one":{"name_one":"one name","id_one":1},"two":{"name_two":"two name","id_2":2},"three":{"name_one":"one name","id_one":1}}}'); + + $resJson = $res->withoutResponseToJsonString(); + var_dump($resJson); + expect($resJson)->toBe('{"one":{"name_one":"one name","id_one":1},"two":{"name_two":"two name","id_2":2},"three":{"name_one":"one name","id_one":1}}'); + + $resJson = $res->toJsonString(); + expect($resJson)->toBe('{"code":-1,"message":"233","data":{"one":{"name_one":"one name","id_one":1},"two":{"name_two":"two name","id_2":2},"three":{"name_one":"one name","id_one":1}}}'); + +}); \ No newline at end of file