makinacorpus / normalization
用于类名到业务名称映射的工具,部分与Symfony和其他工具集成
Requires
- php: >=7.4
Requires (Dev)
- phpunit/phpunit: ^9
- ramsey/uuid: ^3.8 || ^4
- symfony/config: ^6.0|^7.0
- symfony/console: ^6.0|^7.0
- symfony/dependency-injection: ^6.0|^7.0
- symfony/http-kernel: ^6.0|^7.0
- symfony/serializer-pack: ^1.0|^5.0
- symfony/yaml: ^6.0|^7.0
README
PHP类名到业务域名命名策略和规范化辅助工具。
主要功能是名称映射,提供可逆和可预测的类名到业务名称和业务名称到类名转换,其目的是将其置于任何公开PHP类名的组件之前,以便能够将内部类型名称别名到业务域名。
标签
每个名称转换条目都与一个任意标签相关联,其目的是在上下文中消除逻辑名称的歧义,例如考虑您有以下两个类
\App\Entity\Order
逻辑上别名为my_app.order
\App\Command\Order
逻辑上别名为my_app.order
您将有一个名称冲突。为了解决这个问题,可以按以下方式对每个类进行标记
\App\Entity\Order
逻辑上别名为my_app.order
并带有entity
标签,\App\Command\Order
逻辑上别名为my_app.order
并带有command
标签。
在将别名名称转换为PHP类名时,您可以指定当前上下文中的预期标签,从而避免这些歧义。
在通过逻辑名称查询PHP名称类时
- 如果只有一个条目匹配,无论使用哪个标签,则返回此条目,
- 如果找到带有
default
标签的条目,则返回它, - 如果存在多个匹配项且没有带有
default
标签的条目,则引发异常。
PHP类可能用于多个逻辑名称,因此在通过PHP类名查询逻辑名称时适用以下规则
- 如果只有一个条目匹配,无论使用哪个标签,则返回此条目,
- 如果找到带有
default
标签的条目,则返回它, - 如果存在多个匹配项且没有带有
default
标签的条目,则引发异常。
从PHP类名到和从PHP类名的解析算法行为相同。
静态命名策略
使用属性
您可以为单个类提供一个默认名称。
namespace App\Entity; use MakinaCorpus\Normalization\Alias; #[Alias(name: "foo")] class Foo { }
或者使用标签针对某些上下文
namespace App\Command; use MakinaCorpus\Normalization\Alias; #[Alias(name: "do_foo", tag: "command")] class Foo { }
或者如果您愿意,在不同上下文中提供多个名称
namespace App\Command; use MakinaCorpus\Normalization\Alias; #[Alias(name: "foo")] #[Alias(name: "do_foo", tag: "command")] #[Alias(name: "foo_done", tag: "event")] class Foo { }
您也可以在同一个标签或默认标签下提供尽可能多的别名,例如当您重命名业务项并希望保持向后兼容时
namespace App\Command; use MakinaCorpus\Normalization\Alias; #[Alias(name: "do_foo", tag: "command")] #[Alias(name: "older_legacy_name", tag: "command", deprecated: true)] class Foo { }
您也可以简单地提供一个优先级(结果将与使用deprecated
参数相同),但不会在PHP侧引发任何弃用通知
namespace App\Command; use MakinaCorpus\Normalization\Alias; #[Alias(name: "do_foo", tag: "command", priority: 100)] #[Alias(name: "older_legacy_name", tag: "command", priority: -100)] class Foo { }
之前解释的算法将使用所有这些参数工作。
使用Symfony配置
@todo
动态命名策略
这提供了三种不同的类命名策略
-
透传名称转换,不进行任何转换。公开的名称是您的PHP类名。
-
基于前缀的名称转换,将字符串
Foo\Shop\Domain\Order\Command\BasketProductAdd
转换为FooShop.Order.Command.BasketProductAdd
,考虑到Foo\Shop\Domain
命名空间前缀将始终静态转换为FooShop
,并使用.
替换分隔符。 -
静态映射名称转换,使用用户提供的静态映射。
-
当然,您可以使用
MakinaCorpus\Normalization\NameMappingStrategy
接口实现自己的策略。
您可以将名称映射配置为包含无限数量的策略,每个策略通过一个 标签 进行标识,这允许使用此 API 的每个服务都有自己的命名策略。
名称映射允许用户自定义别名映射,它可以包含单个 PHP 类名无限多的名称别名,使您的项目能够抵御过时的约定。例如,当您在消息总线中插入时更改命名约定:您的应用程序可以在升级的同时继续消费旧消息。
此外,它还提供了一些其他辅助工具。
-
一个自定义的
Serializer
接口,具有默认实现,该实现使用symfony/serializer
组件。这使得使用此 API 的代码能够从可替换的序列化器中受益。 -
为
symfony/serializer
提供的ramsey/uuid
正常化和反正常化器。 -
未来可能还会添加更多。
配置
安装此包
composer req makinacorpus/normalization
如果您使用的是 Symfony,请在 config/bundles.php
中添加该束。
<?php return [ // ... Your other bundles. MakinaCorpus\Normalization\Bridge\Symfony\NormalizationBundle::class => ['all' => true], ];
或者,您可以独立设置名称映射。
use MakinaCorpus\Normalization\NameMap\DefaultNameMap; use MakinaCorpus\Normalization\NameMap\PrefixNameMappingStrategy; $nameMap = new DefaultNameMap( new PrefixNameMappingStrategy( 'MyApp', 'My\\Namespace\\Prefix', ) );
Symfony束配置
以下是一个示例 config/packages/normalization.yaml
文件
# # Sample configuration # normalization: default_strategy: # # Default name mapping strategy configuration. # # Per default the "PrefixNameMappingStrategy" is used, which means # that you need to give an application name prefix string, which will # be all normalized names prefix, and a PHP class namespace prefix # that will identify which PHP classes belongs to you or not. # # Per default the app name is "App" and the namespace prefix is # "App" as well, to mimic default Symfony skeleton app. # app_name: MyApp class_prefix: MyVendor\\MyApp strategy: # # Keys here are arbitrary user-defined tags. # # Tags purpose is to allow API user to define different strategies # for different contextes. # # See \MakinaCorpus\Normalization\NameMap::TAG_* constants which # provides a few samples values. # # Values must be container services identifiers. # command: \App\Infra\Normalization\CustomCommandNameMappingStrategy event: \App\Infra\Normalization\CustomEventNameMappingStrategy static: # # Keys here are arbitrary user-defined tags. # # Tags purpose is to allow API user to define different strategies # for different contextes. # # See \MakinaCorpus\Normalization\NameMap::TAG_* constants which # provides a few samples values. # command: # # Actual business domain name to PHP class name conversion. # map: Php\Native\Type: my_app.normalized_name Php\Other\Native\Type: my_app.other_normalized_name # # Legacy aliases to PHP class name conversion. # aliases: Php\Legacy\Name: Php\Native\Type Php\EvenMoreLegacy\Name: Php\Native\Type my_app.legacy_normalized_name: Php\Native\Type my_app.other_legacy_normalized_name: my_app.normalized_name
用法
为了使用名称映射,只需将其服务注入到需要它的服务中即可
namespace App\Infra\Bus; use MakinaCorpus\Normalization\NameMap; class SomeBus { public function __construct( private NameMap $nameMap { } /** * This is fictional pseudo-code. */ public function getClassBusinessName(object $message): void { return $this ->nameMap ->fromPhpType( \get_class($message), 'some_tag' ) ; } }
如果您正在开发一个 Symfony 应用程序,您可以在对象上使用 MakinaCorpus\Normalization\NameMap\NameMapAware
接口,以便容器自动填充它
namespace App\Infra\Bus; use MakinaCorpus\Normalization\NameMap\NameMapAware; use MakinaCorpus\Normalization\NameMap\NameMapAwareTrait; class SomeBus implements NameMapAware { use NameMapAwareTrait; /** * This is fictional pseudo-code. */ public function send(object $message): void { return $this ->getNameMap() ->fromPhpType( \get_class($message), 'some_tag' ) ; } }
测试
在 sys/
文件夹中有一个包含各种容器和各种 PHP 版本的 Docker 环境。为了使测试在所有 PHP 版本中都能工作,您需要运行 composer update --prefer-lowest
,否则 PHP 7.4 测试将失败。
composer install composer update --prefer-lowest cd sys/ ./docker-rebuild.sh # Run this only once ./docker-run.sh