whitedigital-eu / entity-resource-mapper-bundle
将 Doctrine 实体对象映射到 API 资源对象并反向映射的包。
Requires
- php: >=8.1
- api-platform/core: ^3.2
- doctrine/annotations: *
- doctrine/collections: *
- doctrine/common: ^3.4
- doctrine/dbal: ^3.7
- doctrine/orm: ^2.19
- doctrine/persistence: *
- phpdocumentor/reflection-docblock: *
- psr/log: *
- symfony/config: ^6.3
- symfony/console: ^6.3
- symfony/dependency-injection: ^6.3
- symfony/doctrine-bridge: ^6.3
- symfony/framework-bundle: ^6.3
- symfony/http-foundation: ^6.3
- symfony/http-kernel: ^6.3
- symfony/maker-bundle: *
- symfony/property-access: ^6.3
- symfony/property-info: ^6.3
- symfony/security-bundle: ^6.3
- symfony/security-core: ^6.3
- symfony/serializer: ^6.3
- symfony/service-contracts: *
- symfony/string: ^6.3
- symfony/translation: ^6.3
- symfony/validator: ^6.3
- symfony/yaml: ^6.3
Requires (Dev)
- phpunit/phpunit: ^10.0
- roave/security-advisories: dev-latest
- whitedigital-eu/config-pack: ^2
- dev-main / 0.x-dev
- 0.25.x-dev
- 0.24.13
- 0.24.12
- 0.24.11
- 0.24.10
- 0.24.9
- 0.24.8
- 0.24.7
- 0.24.6
- 0.24.5
- 0.24.4
- 0.24.3
- 0.24.2
- 0.24.1
- 0.24.0
- 0.23.24
- 0.23.23
- 0.23.22
- 0.23.21
- 0.23.20
- 0.23.19
- 0.23.18
- 0.23.17
- 0.23.16
- 0.23.15
- 0.23.14
- 0.23.13
- 0.23.12
- 0.23.11
- 0.23.10
- 0.23.9
- 0.23.8
- 0.23.7
- 0.23.6
- 0.23.5
- 0.23.4
- 0.23.3
- 0.23.2
- 0.23.1
- 0.23.0
- 0.22.1
- 0.22.0
- 0.21.x-dev
- 0.21.10
- 0.21.9
- 0.21.8
- 0.21.7
- 0.21.6
- 0.21.5
- 0.21.4
- 0.21.3
- 0.21.2
- 0.21.1
- 0.21.0
- 0.20.3
- 0.20.2
- 0.20.1
- 0.20.0
- 0.19.x-dev
- 0.19.4
- 0.19.3
- 0.19.2
- 0.19.1
- 0.18.2
- 0.18.1
- 0.18.0
- 0.17.1
- 0.17.0
- 0.16.x-dev
- 0.16.8
- 0.16.7
- 0.16.6
- 0.16.5
- 0.16.4
- 0.16.3
- 0.16.2
- 0.16.1
- 0.16.0
- 0.15.4
- 0.15.3
- 0.15.2
- 0.15.1
- 0.15.0
- 0.14.9
- 0.14.8
- 0.14.7
- 0.14.6
- 0.14.5
- 0.14.4
- 0.14.3
- 0.14.2
- 0.14.1
- 0.14.0
- 0.13.2
- 0.13.1
- 0.13.0
- 0.12.1
- 0.12.0
- 0.11.2
- 0.11.1
- 0.11.0
- dev-fix/improve-find-by-id
- dev-fix/remove-invalid-attribute-from-filter-openapi-schema
- dev-fix/redundancy
- dev-fix/return-correct-type-utc-datetimeimmutable
- dev-feature/in-filter
- dev-fix/allow-groups-attribute
- dev-feature/symfony-6.3-support
- dev-ft/fix-col-post-limited-grant
- dev-ft/circular-reference-override-tests
This package is auto-updated.
Last update: 2024-09-29 11:53:16 UTC
README
-
通过帮助将 Doctrine 实体对象与 Api Platform 资源对象进行映射,扩展了 Symfony / Api Platform 的功能,并提供其他辅助功能,例如过滤器、JSON 函数等。
-
实现 AuthorizationService,该服务集中管理所有授权配置,并提供授权资源的方法
- 数据提供者 - 集合获取
- 数据提供者 - 项目获取
- 数据持久化 - 项目 post/put/patch
- 数据持久化 - 项目删除
- EntityToResourceMapper 中的单个资源
要求
PHP 8.1+
Symfony 6.3+
安装
推荐使用 Composer 安装
composer require whitedigital-eu/entity-resource-mapper-bundle
配置
ClassMapper 服务
您应该创建 ClassMapper 服务配置文件,例如
namespace App\Service; use App\Dto\CustumerDto; use App\Entity\Customer; use WhiteDigital\EntityResourceMapper\Mapper\ClassMapperConfiguratorInterface; use WhiteDigital\EntityResourceMapper\Mapper\ClassMapper; class ClassMapperConfigurator implements ClassMapperConfiguratorInterface { public function __invoke(ClassMapper $classMapper) { $classMapper->registerMapping(CustomerResource::class, Customer::class); // with Callback - must return true for mapping to be active $classMapper->registerMapping(PublicHtmlResource::class, Html::class, callback: static fn (array $context) => !self::isAdmin($context)); $classMapper->registerMapping(AdminHtmlResource::class, Html::class, callback: static fn (array $context) => self::isAdmin($context)); } /** * IsAdmin or else IsPublic. */ private static function isAdmin(array $context): bool { return array_key_exists('request_uri', $context) && str_starts_with($context['request_uri'], '/api/admin'); } }
并在您的 services.yaml 文件中将它注册为 ClassMapper 服务的配置器
WhiteDigital\EntityResourceMapper\Mapper\ClassMapperConfiguratorInterface: class: App\Service\ClassMapperConfigurator
此外,您还可以使用 Mapping 属性来注册映射
use App\Dto\CustumerDto; use Doctrine\ORM\Mapping as ORM; use WhiteDigital\EntityResourceMapper\Attribute\Mapping; #[ORM\Entity] #[Mapping(CustumerDto::class)] class Customer ...
use WhiteDigital\EntityResourceMapper\Attribute\Mapping; use App\Entity\Customer; #[Mapping(Customer::class)] class CustumerDto ...
过滤器
目前有以下过滤器可用(过滤器的工作方式如 Api Platform 文档中所述,但以下有注释)
- ResourceBooleanFilter
- ResourceDateFilter (如果值不是有效的 DateTime 对象,则抛出异常)
- ResourceEnumFilter (与 SearchFilter 相同,但有明确的文档)
- ResourceExistsFilter
- ResourceJsonFilter (新过滤器)
- ResourceNumericFilter
- ResourceOrderFilter (允许按 JSON 值排序)
- ResourceOrderCustomFilter (按自定义 SELECT 字段排序的过滤器,这些字段既不在根别名中,也不在连接中)
- ResourceRangeFilter
- ResourceSearchFilter
JSON 函数
以下 PostgreSQL 函数在 Doctrine 中可用,并在 ResourceJsonFilter 和 ResourceOrderFilter 中使用
- JSONB_PATH_EXISTS(%s, %s) - PostgreSQL 函数 jsonb_path_exists(%s::jsonb, %s)
- JSON_GET_TEXT(%s, %s) - PostgreSQL 别名 %s->%s
- JSON_ARRAY_LENGTH(%s) - PostgreSQL 函数 json_array_length(%s)
- JSON_CONTAINS(%s, %s) - PostgreSQL 别名 %s::jsonb @> '%s'
DBAL 类型
此包包含并自动配置以下 dbal 类型以使用 UTC 时区
- date
- datetime
- date_immutable
- datetime_immutable
安全性
可用的操作类型
AuthorizationService::ALL
包括以下所有内容AuthorizationService::COL_GET
集合 GETAuthorizationService::ITEM_GET
项目 GETAuthorizationService::COL_POST
集合 POSTAuthorizationService::ITEM_PATCH
项目 PUT + PATCHAuthorizationService::ITEM_DELETE
项目 DELETE
可用的授权类型
GrantType::ALL
资源完全可用GrantType::LIMITED
资源有局限性可用GrantType::NONE
资源不可用
必须实现 AuthorizationService 配置器。
// src/Service/Configurator/AuthorizationServiceConfigurator.php use WhiteDigital\EntityResourceMapper\Resource\BaseResource; use WhiteDigital\EntityResourceMapper\Security\AuthorizationServiceConfiguratorInterface; final class AuthorizationServiceConfigurator implements AuthorizationServiceConfiguratorInterface { public function __invoke(AuthorizationService $service): void { $service->setAuthorizationOverride(static fn (BaseEntity|BaseResource|null $object = null) => 'cli' === strtolower(PHP_SAPI) && 'test' !== $_ENV['APP_ENV']); $service->setResources([ ActivityResource::class => [ AuthorizationService::ALL => ['ROLE_SUPER_ADMIN' => GrantType::ALL, 'ROLE_KAM' => GrantType::ALL], AuthorizationService::COL_GET => [, 'ROLE_JUNIOR_KAM' => GrantType::OWN], AuthorizationService::ITEM_GET => [, 'ROLE_JUNIOR_KAM' => GrantType::GROUP], AuthorizationService::COL_POST => [], AuthorizationService::ITEM_PATCH => [], AuthorizationService::ITEM_DELETE => [], ]]); //either mainResource or roles key must be set $service->setMenuStructure( [ ['name' => 'ACTIVITIES', 'mainResource' => ActivityResource::class, ], ['name' => 'REPORTS', 'roles' => ['ROLE_SUPER_ADMIN', 'ROLE_KAM'], ], ]); } }
将其注册为服务
WhiteDigital\EntityResourceMapper\Security\AuthorizationServiceConfiguratorInterface:
class: AuthorizationServiceConfigurator
如果设置了 setAuthorizationOverride
闭包,它将使用当前对象(资源或实体)进行调用,如果返回 true,则跳过授权。
使用以下方法
- 在 DataProvider 中,getCollection
$this->authorizationService->limitGetCollection($resourceClass, $queryBuilder); // This will affect queryBuilder object
- 在 DataProvider 中,getItem
$this->authorizationService->authorizeSingleObject($entity, AuthorizationService::ITEM_GET); // This will throw AccessDeniedException if not authorized
- 在 DataPersister 中,persist
$this->authorizationService->authorizeSingleObject($data, AuthorizationService::ITEM_PATCH); // This will throw AccessDeniedException if not authorized // or $this->authorizationService->authorizeSingleObject($data, AuthorizationService::COL_POST; // This will throw AccessDeniedException if not authorized
- 在 DataPersister 中,remove
$this->authorizationService->authorizeSingleObject($data, AuthorizationService::ITEM_DELETE); // This will throw AccessDeniedException if not authorized
- 在任意 Resource 中,如果您已将其授权定义为 LIMITED,则必须向 BaseResource 类添加属性,以定义资源类中每个类的访问解析器配置
#[AuthorizeResource(accessResolvers: [ new AccessResolverConfiguration(className: OwnerPropertyAccessResolver::class, config: ['ownerPropertyPath' => 'supervisor']), ])]
同一类还必须使用以下属性设置正确的规范化组
#[Groups('deal_read')] #[ApiProperty(attributes: ["openapi_context" => ["description" => "If Authorization GrantType::OWN or GROUP is calculated, resource can be restricted."]])] public bool $isRestricted = false;
属性可见性检查
有时您希望返回端点中的所有项目,但要根据用户角色限制返回的属性。为此,您需要将 GrantType::LIMITED
设置为希望进行此可见性检查的角色和操作,并将 #[VisibleProperty]
属性添加到需要进行此检查的资源。 #[VisibleProperty]
属性接受两个参数: ownerProperty
和 properties
。 properties
是您希望 SHOW
的所有属性的数组。ownerProperty
是要检查与当前登录用户的属性名称。
重要:如果资源对某些角色设置了 GrantType::LIMITED 以进行获取或获取集合操作,则至少必须设置一个访问解析器或
#[VisibleProperty]
!
显式检查是否在授权服务中配置了所有角色
如果您想显式检查是否在授权服务中完全配置了所有项目定义的角色,可以通过传递包含所有所需角色的 BackedEnum 来配置此检查。
默认值是
[]
,因此没有此配置检查将不会被触发。
<?php declare(strict_types = 1); use App\Constants\Enum\Roles; use Symfony\Config\EntityResourceMapperConfig; return static function (EntityResourceMapperConfig $config): void { $config ->rolesEnum(Roles::class); };
或者
entity_resource_mapper: roles_enum: App\Constants\Enum\Roles
此枚举必须支持并包含所有所需的角色,并带有 ROLE_
前缀,如下所示
<?php declare(strict_types = 1); namespace App\Constants\Enum; enum Roles: string { case ROLE_USER = 'ROLE_USER'; case ROLE_ADMIN = 'ROLE_ADMIN' }
现在,如果您在 AuthorizationService->setServices()
中为任何资源操作配置了 ROLE_USER 或 ROLE_ADMIN 授权,则会抛出异常。
公共资源访问
如果需要未经授权访问任何资源(默认情况下这是禁止的),您可以使用 AuthorizationServiceConfigurator
允许为 AuthenticatedVoter::PUBLIC_ACCESS
允许特定操作。为此,使用 GrantType::ALL
配置所需操作。仅允许使用 GrantType::ALL
(不允许使用 GrantType::LIMITED
),并且您不需要为公共访问设置 GrantType::NONE
。示例
// src/Service/Configurator/AuthorizationServiceConfigurator.php use WhiteDigital\EntityResourceMapper\Security\AuthorizationServiceConfiguratorInterface; use WhiteDigital\EntityResourceMapper\Security\Enum\GrantType; use Symfony\Component\Security\Core\Authorization\Voter\AuthenticatedVoter; final class AuthorizationServiceConfigurator implements AuthorizationServiceConfiguratorInterface { public function __invoke(AuthorizationService $service): void { $service->setResources([ ActivityResource::class => [ AuthorizationService::ALL => ['ROLE_SUPER_ADMIN' => GrantType::ALL, 'ROLE_KAM' => GrantType::ALL], AuthorizationService::COL_GET => ['ROLE_JUNIOR_KAM' => GrantType::LIMITED], AuthorizationService::ITEM_GET => [AuthenticatedVoter::PUBLIC_ACCESS => GrantType::ALL], AuthorizationService::COL_POST => [], AuthorizationService::ITEM_PATCH => [], AuthorizationService::ITEM_DELETE => [], ]]); } }
菜单构建器
此软件包附带菜单构建器功能,允许定义整体菜单结构,并允许根据当前用户限制(授权、规则)动态构建菜单。
要使用菜单构建器服务,您必须首先创建一个实现 WhiteDigital\EntityResourceMapper\Interface\MenuBuilderServiceConfiguratorInterface 的配置器类。
use WhiteDigital\EntityResourceMapper\MenuBuilder\Interface\MenuBuilderServiceConfiguratorInterface; use WhiteDigital\EntityResourceMapper\MenuBuilder\Services\MenuBuilderService; final class MenuBuilderServiceConfigurator implements MenuBuilderServiceConfiguratorInterface { public function __invoke(MenuBuilderService $service): void { //either mainResource or roles key must be set $service->setMenuStructure( [ [ 'name' => 'ACTIVITIES', 'mainResource' => ActivityResource::class, ], [ 'name' => 'REPORTS', 'roles' => ['ROLE_SUPER_ADMIN', 'ROLE_KAM'], ], ]); } }
将配置器类注册为服务
WhiteDigital\EntityResourceMapper\MenuBuilder\Interface\MenuBuilderServiceConfiguratorInterface:
class: MenuBuilderServiceConfigurator
最后,您可以使用 menubuilder 并通过调用 MenuBuilderService 以这种方式检索过滤后的菜单:
use WhiteDigital\EntityResourceMapper\MenuBuilder\Services\MenuBuilderService; class SomeClass { public function someFunction(MenuBuilderService $service): void { $data = $service->getMenuForCurrentUser(); } }
基本提供程序和处理器
在大多数情况下,读取或写入数据库的方法是相同的,因此此软件包提供了 AbstractDataProcessor
和 AbstractDataProvider
,它们实现了 api 平台的基础逻辑。此软件包的构建部分也使用这些类进行生成。使用这些抽象可以消除为每个实体/资源重复代码的需要。由于这些是抽象,因此您始终可以在需要时覆盖它们的任何功能。
扩展 API 资源
其他 whitedigital-eu
软件包可能附带 API 资源,这些资源在配置某些配置后可能不适合直接在项目中使用。这就是为什么 ExtendedApiResource
对覆盖默认属性中定义的部分非常有用。
例如,看一下 WhiteDigital\Audit\ApiResource\AuditResource
类。它定义了 API 资源。如果您想将其 iri 设置为 /api/vendor/audits
,您必须执行以下操作:
- 创建一个新的类,该类扩展了您想要覆盖的资源
- 添加
ExtendedApiResouce
属性而不是ApiResource
属性 - 仅传递您想要覆盖的选项,其他选项将从扩展的资源中获取
namespace App\ApiResource; use WhiteDigital\EntityResourceMapper\Attribute\ExtendedApiResource; #[ExtendedApiResource(routePrefix: '/vendor')] class AuditResource extends WhiteDigital\Audit\ApiResource\AuditResource { }
ExtendedApiResouce
属性检查您正在扩展的资源,并覆盖了在扩展中给出的选项,同时保持其他选项与父资源中的选项相同。
重要:您需要使用api_platform.openapi.factory装饰器禁用捆绑资源,否则您将有两个审计资源实例:一个带有
/api/audits
iri,另一个带有/api/vendor/audits
iri。
ApiResource生成器
默认配置选项基于api-platform
和symfony
推荐,但您可以像这样覆盖它们(显示默认值)
api_resource: namespaces: api_resource: ApiResource class_map_configurator: Service\\Configurator # required by whitedigital-eu/entity-resource-mapper-bundle data_processor: DataProcessor data_provider: DataProvider entity: Entity root: App defaults: api_resource_suffix: Resource role_separator: ':' space: '_'
use Symfony\Config\EntityResourceMapperConfig; return static function (EntityResourceMapperConfig $config): void { $namespaces = $config ->namespaces(); $namespaces ->apiResource('ApiResource') ->classMapConfigurator('Service\\Configurator') # required by whitedigital-eu/entity-resource-mapper-bundle ->dataProcessor('DataProcessor') ->dataProvider('DataProvider') ->entity('Entity') ->root('App'); $defaults = $config ->defaults(); $defaults ->apiResourceSuffix('Resource') ->roleSeparator(':') ->space('_'); };
namespaces
用于设置生成的文件的目录。因此,如果您需要将文件放在不同的目录/命名空间中,您可以这样更改它。
roleSeparator
和来自defaults
的space
用于配置api资源中使用的分隔符。例如,默认情况下,带有默认值的UserRole
将变为read组的user_role:read
。
apiResourcrSuffix
定义api资源类名的后缀。例如,默认情况下,User
实体将生成UserResource
api资源类。
用法
只需运行make:api-resource <实体名>
,其中实体名是要为其创建api资源的实体。例如,运行make:api-resource User
来为User实体创建UserResource,UserDataProcessor和UserDataProvider。
生成器命令根据实体变量生成资源属性。这有时可能是错误的或不必要的,因此您可以传递--no-properties
选项来不生成属性。
默认情况下,生成器命令在尝试生成已存在的类时会抛出错误。如果您出于某些原因想要重写生成的类,可以传递--delete-if-exists
选项。
有时这个选项很有用,当您有两个具有关系的实体。由于特定的逻辑不可能,要自动为两个类生成资源,您应该
- 运行
make:api-resource Entity1 --no-properties
- 运行
make:api-resource Entity2
- 运行
make:api-resource Entity1 --delete-if-exists
此命令自动为给定实体生成ApiFilters。默认值为生成第一级字段。例如
use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiResource; use WhiteDigital\EntityResourceMapper\Filters\ResourceDateFilter; use WhiteDigital\EntityResourceMapper\Resource\BaseResource; #[ ApiResource ( shortName: 'User' ), ApiFilter(ResourceDateFilter::class, properties: ['createdAt', 'updatedAt', ]), ] class UserResource extends BaseResource { public ?DateTimeImmutable $createdAt = null; public ?DateTimeImmutable $updatedAt = null; public ?UserResource $parent = null; }
如果您不想生成任何过滤器,请通过传递level 0
来运行命令
bin/console make:api-resource User --level 0
如果您想为子资源生成更多级别的过滤器,例如parent.createdAt,请传递更高级别
bin/console make:api-resource User --level 2
use ApiPlatform\Metadata\ApiFilter; use ApiPlatform\Metadata\ApiResource; use WhiteDigital\EntityResourceMapper\Filters\ResourceDateFilter; use WhiteDigital\EntityResourceMapper\Resource\BaseResource; #[ ApiResource ( shortName: 'User' ), ApiFilter(ResourceDateFilter::class, properties: ['createdAt', 'updatedAt', 'parent.createdAt', 'parent.updatedAt']), ] class UserResource extends BaseResource { public ?DateTimeImmutable $createdAt = null; public ?DateTimeImmutable $updatedAt = null; public ?UserResource $parent = null; }
更高级别 -> 更深的子资源过滤器。
很明显,您可能不需要所有生成的过滤器,但删除它们比添加它们要容易。
如果您想排除特定类型的过滤器,可以传递--exclude-<filter>
来跳过这些过滤器的生成。
bin/console make:api-resource User --level 2 --exclude-array --exclude-numeric --exclude-range
可用的过滤器包括
array
:ResourceJsonFilter
bool
:ResourceBooleanFilter
date
:ResourceDateFilter
enum
:ResourceEnumFilter
numeric
:ResourceNumericFilter
range
:ResourceRangeFilter
search
:ResourceSearchFilter
ResourceOrderFilter
由非排除的numeric
、search
、date
和array
过滤器创建。
PHP CS Fixer
重要:在运行php-cs-fixer时,请确保不要格式化
skeleton
文件夹中的文件。否则,生成器命令将停止工作。
验证器
此库包含Classifiers的验证器。这些验证器仅在实体/资源具有以下结构(不少于此)时才起作用:实体
use Doctrine\ORM\Mapping\Entity; #[Entity] class Classifier { private ?int $id = null; private ?string $value = null; private ?array $data = []; private ?ClassifierType $type = null; }
资源
use ApiPlatform\Metadata\ApiResource; #[ApiResource] class ClassifierResource { public mixed $id = null; public ?string $value = null; public ?array $data = []; public ?ClassifierType $type = null; }
如这些示例所示,您需要一个受支持的枚举(在这里称为ClassifierType
),并对其进行验证。ClassifierType
的示例如下
enum ClassifierType: string { case ONE = 'ONE'; case TWO = 'TWO'; }
现在您可以使用CorrectClassifierType
或ClassifierRequiredDataIsSet
验证器
CorrectClassifierType
:CorrectClassifierType检查在相关资源中是否给出了正确的Classifier类型
use ApiPlatform\Metadata\ApiResource; use WhiteDigital\EntityResourceMapper\Validator\Constraints as WDAssert; #[ApiResource] class TestResource { #[WDAssert\CorrectClassifierType(ClassifierType::ONE)] public ?ClassifierResource $one = null; }
现在如果传递的资源具有任何其他类型(例如,ClassifierType::TWO
),则会抛出错误。
ClassifierRequiredDataIsSet
:有时在分类器中可能需要额外数据,这可能是有必要的。为此,可以使用ClassifierRequiredDataIsSet
来检查这些数据是否已传递。这用于ClassifierResource
。
use ApiPlatform\Metadata\ApiResource; use WhiteDigital\EntityResourceMapper\Validator\Constraints as WDAssert; #[ApiResource] #[WDAssert\ClassifierRequiredDataIsSet(ClassifierType::ONE, ['test1'])] class ClassifierResource { public mixed $id = null; public ?string $value = null; public ?array $data = []; public ?ClassifierType $type = null; }
现在当创建一个类型为ONE
的新分类器时,如果数据不包含键为test1
的值,将会抛出错误。
测试
通过以下方式运行测试
$ vendor/bin/phpunit
待办事项
- 性能改进
- 在dataprovider上的显式连接
- 计算属性作为查询构建器方法