codememory / entity-response-control
根据约束控制实体或其他对象返回的响应
v4.0
2024-07-18 19:36 UTC
Requires
- php: >=8.3
- codememory/reflection: ^3.0
- symfony/string: ^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.22
- symfony/var-dumper: ^6.2
This package is auto-updated.
Last update: 2024-09-18 19:56:59 UTC
README
该库旨在简化API响应的原型设计。其中,您的类作为您提供的输入对象的响应原型参与
安装
$ composer require codememory/entity-response-control
本文档将涵盖哪些内容?
- 如何创建您的ResponsePrototype?
- 有哪些类型的装饰器?
- 有哪些装饰器存在?
- 如何创建您自己的Collector?
- 如何创建上下文工厂?
- 如何创建您自己的键命名策略?
- 如何创建您自己的原型属性提供者?
让我们创建我们的ResponsePrototype
[ ! ] 注意,在ResponsePrototype中,我们处理的所有属性都必须具有"private"访问修饰符,这是默认的,如果您想更改创建,请编写您提供者的属性。如何创建提供者将在稍后介绍
use Codememory\EntityResponseControl\Decorators as RCD; use Codememory\EntityResponseControl\AbstractResponsePrototype; use Codememory\EntityResponseControl\Collectors\BaseCollector; use Codememory\Reflection\ReflectorManager; use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Codememory\EntityResponseControl\Factory\ConfigurationFactory; use Codememory\EntityResponseControl\Factory\ExecutionContextFactory; use Codememory\EntityResponseControl\DecoratorHandlerRegistrar; // We have some entity User class User { private int $id = 1; private string $name = 'My Name'; public function getId(): int { return $this->id; } public function getName(): string { return $this->name; } public function getCreatedAt(): DateTimeInterface { return new DateTime(); } } // Our ResponsePrototype class UserResponsePrototype extends AbstractResponsePrototype { private ?int $id = null; private ?string $name = null; #[RCD\DateTime] // The default format is Y-m-d H:i:s private ?string $createdAt = null; } $userResponse = new UserResponsePrototype( new BaseCollector(), new ConfigurationFactory(), new ExecutionContextFactory(), new DecoratorHandlerRegistrar(), new ReflectorManager(new FilesystemAdapter('entity-response-control', '/var/cache/codememory')) ); $response = $userResponse->collect(new User())->toArray(); // We get the answer in the form of an array: [ "id" => 1, "name" => "My Name", "created_at" => "2023-01-03 00:00" ]
这只是一个简单的例子,在实际项目中,您可以根据用户的权限或请求的类型等控制每个属性
让我们看看装饰器
- AliasInResponse - 在响应中显示不同的名称
- $name: string - 响应中的属性名称
- Prefix - 更改调用方法的名称前缀(默认为get)或更改响应中的名称前缀
- $prototypeObject: string | null - 获取值的原型对象的getter方法前缀
- $responsePrefix: string | null - 响应中的前缀
- Custom - 自定义属性,调用get方法将被忽略
- $methodName: string - 方法名称
- HiddenNullable - 隐藏响应中具有null值的属性
- $ignoreEmptyString (默认: true): bool - 如果设置为false,则具有空字符串的属性也将从响应中隐藏
- Count - 如果属性是数组或实现Countable接口,则调用计数方法,如果值是字符串,则计算字符串的长度,响应类型始终为整数
- ArrayValues - 将多维数组或对象数组转换为值数组
- $key: string - 数组键的名称或要调用的方法的名称
- Callback - 创建您自己的回调,此方法必须在您的ResponseControl内部以公共访问修饰符创建
- $methodName: string - 方法名称
- NestedPrototype - 将属性值传递给另一个ResponseControl。请注意使用最后的参数之一,以免产生循环依赖
- $prototype: string - ResponseControl类的命名空间
- $skipProperties: array - 忽略一些来自$prototype的属性
- $skipAllPropertiesExpect: array - 忽略$prototype中的所有属性,但列出那些除外
- DateTime - 期望属性值是DateTimeInterface接口,如果是,则给定对象将转换为默认格式或您指定的格式
- $format: string - default(Y-m-d H:i:s) - 格式化日期
- $full: bool - 默认(false) - 如果为true,则返回一个包含完整信息的DateTime数组,而不是字符串
- XSS - 保护输入字符串或数组中的字符串免受XSS攻击
- FromEnum - 返回一个由枚举的键和标签组成的数组
- $enum: string|null - 如果值是字符串,则为命名空间枚举,如果值已经是枚举对象,则留空
- CropString - 压缩字符串到最大长度
- $maxlength: INT - 字符串最大长度
- $end: string - 默认(...) - 如果字符串被截断,则在字符串末尾的字符
- PrototypeObjectGetterMethod - 设置从原型对象中获取值的新名称获取器名称
- $name: string - 方法名称
创建自己的装饰器
use Attribute; use Codememory\EntityResponseControl\Interfaces\DecoratorInterface; use Codememory\EntityResponseControl\Interfaces\DecoratorHandlerInterface; use Codememory\EntityResponseControl\Interfaces\ExecutionContextInterface; use Symfony\Component\String\u; // This decorator will change the getter prefix #[Attribute(Attribute::TARGET_PROPERTY)] final class MyDecorator implements DecoratorInterface { public function __construct ( public readonly string $prefix ) {} public function getHandler() : string { return MyDecoratorHandler::class; } } // Decorator handler final class MyDecoratorHandler implements DecoratorHandlerInterface { // This method can return any result. public function handle(DecoratorInterface $decorator, ExecutionContextInterface $context) : void { $propertyName = $context->getProperty()->getName(); $context->setNameGetterToGetValueFromObject(u("{$decorator->prefix}_{$propertyName}")->camel()); // Example: isPay // Update the value by getting it from the new method $context->setValue($context->getPrototypeObject()->{$context->getNameGetterToGetValueFromObject()}()); } }
注册装饰器
// Before calling collect, refer to the configuration $responsePrototype->getDecoratorHandlerRegistrar()->register(new MyDecoratorHandler()); // Collect prototype...
考虑创建自己的收集器
use Codememory\Reflection\ReflectorManager; use Symfony\Component\Cache\Adapter\FilesystemAdapter; use Codememory\EntityResponseControl\Interfaces\CollectorInterface; use Codememory\EntityResponseControl\Interfaces\ResponsePrototypeInterface; use Codememory\Reflection\Reflectors\PropertyReflector; use Codememory\EntityResponseControl\Interfaces\DecoratorInterface; use Codememory\EntityResponseControl\Factory\ExecutionContextFactory; use Codememory\EntityResponseControl\Factory\ConfigurationFactory; use Codememory\EntityResponseControl\DecoratorHandlerRegistrar; class MyObjectCollector implements CollectorInterface { public function collect(ResponsePrototypeInterface $responsePrototype, object $prototypeObject, array $properties): array { $collectedResponse = []; foreach ($properties as $property) { if ($property instanceof PropertyReflector) { // Create a context $context = $responsePrototype->getExecutionContextFactory()->createExecutionContext($responsePrototype, $property, $prototypeObject); foreach ($property->getAttributes() as $attribute) { $decorator = $attribute->getInstance(); if ($decorator instanceof DecoratorInterface) { // Getting a decorator handler $decoratorHandler = $responsePrototype->getDecoratorHandlerRegistrar()->getHandler($decorator->getHandler()); // Calling a decorator handler $decoratorHandler->handle($decorator, $context); } } // Collecting an array of data $collectedResponse[$context->getResponseKey()] = $context->getValue(); } } return $collectedResponse; } } // An example of using our UserPrototype with the new Collector $userResponse = new UserResponse( new MyObjectCollector(), new ConfigurationFactory(), new ExecutionContextFactory(), new DecoratorHandlerRegistrar(), new ReflectorManager(new FilesystemAdapter('entity-response-control', '/var/cache/codememory')) ); $userResponse ->collect([new User()]) ->toArray(); // Response to array
如何创建上下文工厂?
use Codememory\EntityResponseControl\Interfaces\ExecutionContextInterface; use Codememory\EntityResponseControl\Interfaces\ExecutionContextFactoryInterface; use Codememory\EntityResponseControl\Interfaces\ResponsePrototypeInterface; use Codememory\Reflection\Reflectors\PropertyReflector; // Create a context final class MyContext implements ExecutionContextInterface { // Implementing Interface Methods... } // Creating a context factory final class MyContextFactory implements ExecutionContextFactoryInterface { public function createExecutionContext(ResponsePrototypeInterface $responsePrototype, PropertyReflector $property, object $prototypeObject): ExecutionContextInterface { $context = new MyContext(); // ... return $context; } }
如何创建您自己的键命名策略?
此策略将在传递给collect的作为 "_{原型属性名称}" 的数据中查找值
use Codememory\EntityResponseControl\Interfaces\ResponseKeyNamingStrategyInterface; final class MyStrategyName implements ResponseKeyNamingStrategyInterface { private ?\Closure $extension = null; public function convert(string $propertyName) : string { $name = "_$propertyName"; if (null !== $this->extension) { return call_user_func($this->extension, $name); } return $name; } // With this method, you need to give the opportunity to extend the convert method public function setExtension(callable $callback) : ResponseKeyNamingStrategyInterface { $this->extension = $callback; return $this; } } $myPrototype = new MyResponsePrototype(new BaseCollector(), new ConfigurationFactory(), ...); // To use this strategy, you need to change the configuration $myPrototype->getConfiguration()->setDataKeyNamingStrategy(new MyStrategyName());
如何创建自己的ResponsePrototype属性提供者?
提供者必须返回允许由收集器处理的dto属性!不要忘记忽略 AbstractResponsePrototype 属性,否则这些属性也会被处理
use Codememory\EntityResponseControl\Provider\ResponsePrototypePrivatePropertyProvider; use Codememory\Reflection\Reflectors\ClassReflector; // The provider will say that only private properties need to be processed final class MyPropertyProvider implements ResponsePrototypePrivatePropertyProvider { private ?\Closure $extension = null; public function getProperties(ClassReflector $classReflector) : array { $properties = $classReflector->getPrivateProperties(); if (null !== $this->extension) { return call_user_func($this->extension, $properties); } return $properties; } // With this method, you need to give the opportunity to extend the getProperties method public function setExtension(callable $callback) : ResponsePrototypePrivatePropertyProvider { $this->extension = $callback; return $this; } } $myPrototype = new MyResponsePrototype(new BaseCollector(), new ConfigurationFactory(), ...); // Change the provider in the configuration $myPrototype->getConfiguration()->setResponsePrototypePropertyProvider(new MyPropertyProvider());