elazar / phanua
从OpenAPI 3组件模式构建Cycle ORM模式
Requires
- php: >=7.4
- cycle/orm: ^1.5
- cycle/schema-builder: ^1.2
- jane-php/open-api-3: ^7.1
- pimple/pimple: ^3.4
- psr/container: ^1
- psr/log: ^1.1
Requires (Dev)
- ext-pdo_sqlite: *
- friendsofphp/php-cs-fixer: ^3.0
- mockery/mockery: ^1.4
- pestphp/pest: ^1.8
This package is auto-updated.
Last update: 2022-12-29 03:39:17 UTC
README
OpenAPI 3 + Jane + Cycle ORM = 🔥
Phanua从OpenAPI 3组件模式构建Cycle ORM模式。
根据MIT许可证发布。
警告:此项目处于开发alpha阶段,可能会发生破坏向后兼容性的更改。它可能包含错误,缺少功能或扩展点,或者在其他方面不适合生产使用。用户需自行判断。
需求
安装
使用Composer。
composer require elazar/phanua
使用
<?php /** * 1. Configure the Phanua service provider. * * The next section covers this step in more detail. */ $provider = new \Elazar\Phanua\Service\Provider; // Configure $provider as needed here. /** * 2. Use the Phanua service provider to create a Phanua schema builder. */ $schemaBuilder = $provider->getSchemaBuilder(); /** * 3. Use the Phanua schema builder to generate a Cycle schema or ORM instance. */ // To configure an existing ORM instance to use the generated schema: $orm = new \Cycle\ORM\ORM(/* ... */); // ... $schema = $schemaBuilder->buildSchema(); $orm = $orm->withSchema($schema); // To have Phanua create a new ORM instance with the generated schema: $orm = $schemaBuilder->buildOrm();
配置
对于最基本的使用,Phanua需要三个参数
您可以通过使用Phanua服务提供者类Elazar\Phanua\Service\Provider
的实例来传递这些参数给Phanua。
您可以通过使用您的Jane配置文件或直接传递给服务提供者实例来提供路径和命名空间。
<?php use Elazar\Phanua\Service\Provider; // To load the Jane configuration file, provide the path $provider = (new Provider) ->withJaneConfiguration('/path/to/.jane-openapi.php'); // If file is already loaded, provide the contained array $janeConfig = require '.jane-openapi.php'; $provider = (new Provider) ->withJaneConfiguration($janeConfig); // To pass the same values directly: $provider = (new Provider) ->withOpenApiSpecPath('path/to/openapi.json') ->withNamespace('\\Foo\\Generated');
您可以通过指定传递给Spiral\Database\Config\DatabaseConfig
的相同数组来提供数据库配置。请参阅相关Cycle文档以获取此数组的示例。
<?php $databaseConfig = [ /* ... */ ]; $provider = (new \Elazar\Phanua\Service\Provider) ->withDatabaseConfig($databaseConfig);
以其他方式提供数据库配置需要解释Phanua如何处理其依赖项。
覆盖依赖项
要覆盖Phanua的依赖项,您必须提供一个包含该依赖项的依赖注入容器。
Phanua可以使用实现PSR-11标准的任何容器。此类容器的示例是Phanua内部使用的Pimple,如果您尚未使用其他容器,则Pimple是推荐使用的容器。
<?php // Create a Pimple or PSR-11 container instance $container = new \Pimple\Container; // Then configure Phanua to use it $provider = (new \Elazar\Phanua\Service\Provider) ->withDelegateContainer($container);
Phanua 预期这个容器使用完全限定的类名作为入口标识符。以下是如何配置 Pimple 以提供数据库配置的示例方法。
<?php $container = new \Pimple\Container; // Compared to the earlier example of passing the database configuration to // Phanua as an array, this is the next easiest / most low-level method of doing // so if you're not already using a container in your application. use Spiral\Database\Config\DatabaseConfig; $container[DatabaseConfig::class] = fn() => new DatabaseConfig( // Pass the same array passed to Provider->withDatabaseConfig() in the earlier // example here. ); // If you're already using a container and it includes a configured instance of // the DatabaseManager class used by Cycle ORM, you can specify that instead. use Spiral\Database\Config\DatabaseManager; $container[DatabaseManager::class] = fn() => new DatabaseManager( new DatabaseConfig(/* ... */) ); // Or you can specify an implementation of Cycle\ORM\FactoryInterface, such as // the Cycle\ORM\Factory class. use Spiral\Database\Config\{Factory, FactoryInterface}; $container[FactoryInterface::class] = fn() => new Factory( new DatabaseManager(/* ... */) ); // Or you can specify an instance of Cycle\ORM\ORM. use Cycle\ORM\ORM; $container[ORM::class] = fn() => new ORM( new Factory(/* ... */) );
如果你已经在使用 Pimple,并且想让 Phanua 使用你在容器中注册的依赖项,你可以将配置好的 Phanua 服务提供程序实例作为 提供程序 注册到你的容器中。
<?php $provider = new \Elazar\Phanua\Service\Provider; // Configure $provider as needed here. $container = new \Pimple\Container; $container->register($provider);
一旦你使用必要的参数配置了 Phanua 服务提供程序,就可以独立使用它构建的容器。
<?php $provider = new \Elazar\Phanua\Service\Provider; // Configure $provider as needed here. // Pimple $container = $provider->getContainer(); // PSR-11 $psrContainer = $provider->getPsrContainer(); // To get the schema builder: use Elazar\Phanua\Schema\Builder; // Pimple $schemaBuilder = $container[Builder::class]; // PSR-11 $schemaBuilder = $container->get(Builder::class);
角色解析器
Cycle ORM 使用术语 "角色" 来指代实体的唯一名称。
Phanua 为用于确定给定实体角色的类定义了接口 Elazar\Phanua\Entity\RoleResolverInterface
。
此接口包含一个名为 getRole()
的方法,它接收一个参数:一个包含与实体对应的 OpenAPI 组件名称的 string
。
getRole()
必须返回一个包含为实体确定的角色的 string
。
Phanua 默认使用的此接口实现是 Elazar\Phanua\Entity\RoleResolver
。它的 getRole()
方法返回它接收的 OpenAPI 组件名称,因此组件名称和角色相同。
要覆盖默认实现,请使用密钥 Elazar\Phanua\Entity\RoleResolverInterface
向你的容器添加一个条目,并将其解析为实现了该接口的类的实例。
类解析器
Cycle ORM 模式中的实体对应于 类。
Phanua 为用于确定给定实体类的类定义了接口 Elazar\Phanua\Entity\ClassResolverInterface
。
此接口包含一个名为 getClass()
的方法,它接收一个参数:一个包含与实体对应的 OpenAPI 组件名称的 string
。
getClass()
必须返回一个包含实体类的 完全限定名 的 string
。
Phanua 默认使用的此接口实现是 Elazar\Phanua\Entity\ClassResolver
。它的 getClass()
方法返回由 Jane 从 Phanua 服务提供程序配置中使用的命名空间和它接收的 OpenAPI 组件名称构建的类名。
要覆盖默认实现,请使用密钥 Elazar\Phanua\Entity\ClassResolverInterface
向你的容器添加一个条目,并将其解析为实现了该接口的类的实例。
表解析器
每个 Cycle ORM 实体都有一个对应的 表。
Phanua 为用于确定与给定实体关联的表的类定义了接口 Elazar\Phanua\Entity\TableResolverInterface
。
此接口包含一个名为 getTable()
的方法,它接收三个参数:
- 一个包含与实体对应的 OpenAPI 组件名称的
string
; - 代表组件模式的
Jane\Component\OpenApi3\JsonSchema\Model\Schema
实例;以及 - 代表实体的
Cycle\Schema\Definition\Entity
实例。
getTable()
必须返回一个包含实体表名的 string
。
Phanua 默认使用的该接口实现是 Elazar\Phanua\Entity\TableResolver
。它的 getTable()
方法返回实体角色,由当前使用的 Elazar\Phanua\Entity\RoleResolverInterface
实例确定,因此角色和表名相同。有关详细信息,请参阅上面的 "角色解析器" 部分。
要覆盖默认实现,请使用键 Elazar\Phanua\Entity\TableResolverInterface
在您的容器中添加一个条目,并使其解析为实现了该接口的类的实例。
名称解析器
Cycle ORM 中的实体包含具有名称的 字段。
Phanua 为用于确定字段名称的类定义了接口 Elazar\Phanua\Field\NameResolverInterface
。
该接口包含一个名为 getName()
的方法,它接收三个参数
- 一个包含与包含字段的实体对应的 OpenAPI 组件名称的
string
; - 一个包含与字段对应的组件属性的名称的
string
;以及 - 表示属性模式的
Jane\Component\OpenApi3\JsonSchema\Model\Schema
实例。
getName()
必须返回一个包含要分配给字段的名称的 string
。
Phanua 使用的默认该接口实现是 Elazar\Phanua\Field\NameResolver
。它的 getName()
方法返回它接收到的 OpenAPI 属性名称,因此属性名称和字段名称相同。
要覆盖默认实现,请使用键 Elazar\Phanua\Field\NameResolverInterface
在您的容器中添加一个条目,并使其解析为实现了该接口的类的实例。
列解析器
Cycle ORM 实体字段对应于表的 列。
Phanua 为用于确定字段列名称的类定义了接口 Elazar\Phanua\Field\ColumnResolverInterface
。
该接口包含一个名为 getColumn()
的方法,它接收三个参数
- 一个包含与包含字段的实体对应的 OpenAPI 组件名称的
string
; - 一个包含与字段对应的组件属性的名称的
string
;以及 - 表示属性模式的
Jane\Component\OpenApi3\JsonSchema\Model\Schema
实例。
getColumn()
必须返回一个包含要分配给列的名称的 string
。
Phanua 使用的默认该接口实现是 Elazar\Phanua\Field\ColumnResolver
。它的 getColumn()
方法返回它接收到的 OpenAPI 属性名称,因此属性名称和列名称相同。
要覆盖默认实现,请使用键 Elazar\Phanua\Field\ColumnResolverInterface
在您的容器中添加一个条目,并使其解析为实现了该接口的类的实例。
类型解析器
每个 Cycle ORM 实体字段都有一个对应的 抽象类型。
Phanua 为用于确定字段类型的类定义了接口 Elazar\Phanua\Field\TypeResolverInterface
。
该接口包含一个名为 getType()
的方法,它接收三个参数
- 一个包含与包含字段的实体对应的 OpenAPI 组件名称的
string
; - 一个包含与字段对应的组件属性的名称的
string
;以及 - 表示属性模式的
Jane\Component\OpenApi3\JsonSchema\Model\Schema
实例。
getType()
可以返回一个包含要分配给列的类型名称的 string
,或者如果它无法将列解析为类型,则返回 null
。
Phanua 默认使用的此接口实现是 Elazar\Phanua\Field\TypeResolver
。它的 getType()
方法返回从属性的类型和格式以及属性值上的 验证约束(例如,对于数值类型,有 minimum
、exclusiveMinimum
、maximum
或 exclusiveMaximum
;对于字符串/文本值,有 minLength
和 maxLength
)中派生出的类型。如果没有这些约束,getType()
将返回允许最大可能值的最大类型。
注意:此实现不处理嵌套对象、数组或对其他组件架构的引用。这可能在将来发生变化。
如果无法解析到更具体的类型,getType()
支持回退到指定的类型。为了减少常见用例所需的配置,此回退类型的默认值是 'string'
。
如果您想更改此回退类型,例如,您想将 TypeResolver
与自己的解析器实现组合以解析 TypeResolver
无法覆盖的类型,那么您可以将回退类型设置为 null
,以便您的解析器知道何时 TypeResolver
无法解析给定的类型。以下是一个使用 Pimple 容器覆盖回退类型的示例。
<?php use Elazar\Phanua\Field\TypeResolver; $container[TypeResolver::class] = new TypeResolver( null // fallback value goes here );
要使用自己的实现覆盖默认类型解析器,请使用键 Elazar\Phanua\Field\TypeResolverInterface
在容器中添加条目,并使其解析到实现该接口的类的实例。
如果您想使用默认类型解析器、自己的解析器或其他解析器,可以使用 Elazar\Phanua\Field\CompositeFieldResolver
将它们组合起来,该解析器将依次使用解析器,直到其中一个返回类型。
<?php use Elazar\Phanua\Field\CompositeTypeResolver; use Elazar\Phanua\Field\TypeResolver; use Elazar\Phanua\Field\TypeResolverInterface; use My\CustomTypeResolver; // If you want your resolver to be tried first: $container[TypeResolverInterface::class] = new CompositeTypeResolver( new CustomTypeResolver(), new TypeResolver(), ); // If you want the default resolver to be tried first: $container[TypeResolverInterface::class] = new CompositeTypeResolver( new TypeResolver( null // Required for CompositeTypeResolver to fall back to your resolver ), new CustomTypeResolver(), );
主解析器
每个表必须在至少一个列上有一个 主索引,以便编译后的 Cycle ORM 架构包括它。
这种情况是处理从注册表中 编译实体到架构 的编译器的限制:它 跳过了没有主索引的实体。
为了引起对此行为的注意,Phanua 如果无法为给定实体解析主索引,将抛出异常。为了避免这种情况,您必须明确排除任何您不想包含在生成的 Cycle ORM 架构中的实体。下一节将进一步讨论此问题。
Phanua 为用于确定给定字段是否应是其各自列所在的表的主索引部分的类定义了 Elazar\Phanua\Field\PrimaryResolverInterface
接口。
此接口包含一个方法,isPrimary()
,它接收三个参数
- 一个包含与包含字段的实体对应的 OpenAPI 组件名称的
string
; - 一个包含与字段对应的组件属性的名称的
string
;以及 - 表示属性模式的
Jane\Component\OpenApi3\JsonSchema\Model\Schema
实例。
isPrimary()
必须返回一个 boolean
值,其中 true
表示给定字段是相关表的主索引的一部分,而 false
表示它不是。
Phanua 默认使用的此接口实现是 Elazar\Phanua\Field\PrimaryResolver
。其 isPrimary()
方法在给定属性名是 id
时返回 true
,否则返回 false
。此类假定属性和列名相同,这在使用上面 "列解析器" 部分中详细说明的默认 Elazar\Phanua\Field\ColumnResolverInterface
实现时是这种情况。
如果您的属性和列名不一致,或者如果您的任何表中包含一个包含多个列的主索引,请创建自己的接口实现以满足您的数据库需求。
要覆盖默认实现,请使用键 Elazar\Phanua\Field\PrimaryResolverInterface
在容器中添加一个条目,并使其解析为实现了该接口的类的实例。
实体解析器
Phanua 使用接口 Elazar\Phanua\Entity\EntityResolverInterface
表示将 OpenAPI 组件转换为 Cycle ORM 实体的过程。
该接口包含一个方法,getEntity()
,它接收两个参数
- 一个包含与实体对应的 OpenAPI 组件名称的
string
;以及 - 代表组件模式的
Jane\Component\OpenApi3\JsonSchema\Model\Schema
实例。
getEntity()
必须返回一个填充的 Cycle\Schema\Definition\Entity
实例,代表给定的组件对应的实体或 null
如果它无法将组件解析为实体。
Phanua 默认使用的该接口实现是 Elazar\Phanua\Entity\EntityResolver
。它组合了 Elazar\Phanua\Entity\RoleResolverInterface
和 Elazar\Phanua\Entity\ClassResolverInterface
的实现,分别用于确定实体 角色 和 类。
它还允许排除特定组件在生成的模式中有实体,例如那些对应的实体不会有 主索引 的组件。排除一个或多个组件的最简单方法是使用服务提供者:默认情况下,它处理向 EntityResolver
注入回调以过滤组件。
<?php use Jane\Component\OpenApi3\JsonSchema\Model\Schema; $componentFilter = fn ( string $componentName, Schema $componentSchema ): bool => !in_array($componentName, [ // These components are excluded from the schema 'component1', 'component2', // ... ]); $provider = (new \Elazar\Phanua\Service\Provider) ->withComponentFilter($componentFilter);
您还可以通过 覆盖 默认使用的 EntityResolver
实例来排除组件,使用包含回调以在 EntityResolver
的 $filterCallback
构造函数参数中过滤实体或为您自己的实体解析器实现执行类似操作。以下示例使用 Pimple 容器来完成此操作。
<?php use Elazar\Phanua\{ Entity\ClassResolverInterface, Entity\EntityResolver, Entity\EntityResolverInterface, Entity\RoleResolverInterface, Service\Provider }; use Jane\Component\OpenApi3\JsonSchema\Model\Schema; $provider = new Provider; /** * 1. Get a container from the Phanua service provider to override some of the * default dependencies it defines. */ // Pimple $phanuaContainer = $provider->getContainer(); // PSR-11 $phanuaContainer = $provider->getPsrContainer(); /** * 2. In your own container, use the key EntityResolverInterface::class and * assign it an instance of EntityResolver or your own entity resolver * implementation, injecting any default Phanua dependencies that you need. * Below is an example of doing this using Pimple containers. */ $yourContainer = new \Pimple\Container; $yourContainer[EntityResolverInterface::class] = fn() => new EntityResolver( $phanuaContainer[RoleResolverInterface::class], $phanuaContainer[ClassResolverInterface::class], function ( string $componentName, string $propertyName, Schema $propertySchema ): bool { /* return TRUE to include or FALSE to exclude component */ } ); /** * 3. Set your container as the delegate container in the Phanua service * provider. */ $provider = $provider->withDelegateContainer($yourContainer); /** * 4. Get the schema builder as normal. It will use the custom dependencies * you've defined. */ $schemaBuilder = $provider->getSchemaBuilder();
是否让您的实体解析器使用其他 Phanua 依赖项完全是可选的,因为默认的 Phanua 实现就是这样做的;您的实体解析器可以以您喜欢的任何方式运行。
另一种可以考虑的方法是让您的实体解析器实现组合 Phanua 默认使用的实现。通过这样做,您可以使用默认实现来处理与之兼容的实体,并使用您自己的实现来处理那些不兼容的实体。以下示例展示了这种方法。
<?php /** * 1. Define your entity resolver implementation. */ use Cycle\Schema\Definition\Entity; use Elazar\Phanua\Entity\EntityResolverInterface; use Jane\Component\OpenApi3\JsonSchema\Model\Schema; class YourEntityResolver implements EntityResolverInterface { private EntityResolverInterface $entityResolver; // Have the constructor accept another resolver implementation as a // parameter. public function __construct( EntityResolverInterface $entityResolver, /* ... */ ) { $this->entityResolver = $entityResolver; /* ... */ } // Then, in the implementation of the interface method... public function getEntity( string $componentName, Schema $componentSchema ): ?Entity { // $entity = ... // ... if your implementation can't resolve an entity... if ($entity === null) { // ... then have it defer to the injected resolver. $entity = $this->entityResolver->getEntity( $componentName, $componentSchema ); } return $entity; } } /** * 2. Inject your implementation to have Phanua use it. */ // Get the Phanua container and configure your container as normal... $phanuaContainer = $provider->getContainer(); $yourContainer = new \Pimple\Container; $yourContainer[EntityResolverInterface::class] = fn() => new YourEntityResolver( // ... then inject the entity resolver implementation from the Phanua // container into your own implementation. $phanuaContainer[EntityResolverInterface::class] );
字段解析器
Phanua 包含字段解析器,就像它包含实体解析器一样,由 Elazar\Phanua\Field\FieldResolverInterface
表示。
该接口包含一个方法,getField()
,它接收三个参数
- 一个包含与包含字段的实体对应的 OpenAPI 组件名称的
string
; - 一个包含与字段对应的组件属性的名称的
string
;以及 - 表示属性模式的
Jane\Component\OpenApi3\JsonSchema\Model\Schema
实例。
getField()
必须返回一个填充的 Cycle\Schema\Definition\Field
实例,代表给定的属性对应的字段或 null
如果它无法将组件解析为字段。
Phanua 默认使用的该接口实现是 Elazar\Phanua\Field\FieldResolver
。它组合了 Elazar\Phanua\Field\ColumnResolverInterface
、Elazar\Phanua\Field\TypeResolverInterface
和 Elazar\Phanua\Field\PrimaryResolverInterface
的实现,分别用于确定字段的 列、类型 和在 主索引 中的存在。
它还处理设置字段的其它选项,例如其默认值和是否可空,基于对应的属性模式中的 default
和 nullable
值。
FieldResolver
还提供了从生成的模式中排除特定属性的功能。排除一个或多个属性的最简单方法是使用服务提供者:默认情况下,它通过将回调注入到 FieldResolver
中来处理属性过滤。
<?php use Jane\Component\OpenApi3\JsonSchema\Model\Schema; $propertyFilter = fn ( string $componentName, string $propertyName, Schema $propertySchema ): bool => // Exclude all properties with this name $property !== 'propertyName' // Exclude the property only in the specified component && "$component.$property" !== 'componentName.propertyName'; $provider = (new \Elazar\Phanua\Service\Provider) ->withPropertyFilter($propertyFilter);
您还可以通过 重写 默认使用的 FieldResolver
实例为包含在 FieldResolver
的 $filterCallback
构造函数参数中的回调函数的实例来排除属性。以下示例展示了如何使用 Pimple 容器实现这一点。
<?php use Elazar\Phanua\{ Field\ColumnResolverInterface, Field\FieldResolver, Field\FieldResolverInterface, Field\PrimaryResolverInterface, Field\TypeResolverInterface, Service\Provider }; use Jane\Component\OpenApi3\JsonSchema\Model\Schema; $provider = new Provider; /** * 1. Get a container from the Phanua service provider to override some of the * default dependencies it defines. */ // Pimple $phanuaContainer = $provider->getContainer(); // PSR-11 $phanuaContainer = $provider->getPsrContainer(); /** * 2. In your own container, use the key FieldResolverInterface::class and * assign it an instance of your entity resolver implementation, injecting * any default Phanua dependencies that you need. Below is an example of * doing this using Pimple containers. */ $yourContainer = new \Pimple\Container; $yourContainer[FieldResolverInterface::class] = fn() => new FieldResolver( $phanuaContainer[ColumnResolverInterface::class], $phanuaContainer[PrimaryResolverInterface::class], $phanuaContainer[TypeResolverInterface::class], function ( string $componentName, string $propertyName, Schema $propertySchema ): bool { /* return TRUE to include or FALSE to exclude property */ } ); /** * 3. Set your container as the delegate container in the Phanua service * provider. */ $provider = $provider->withDelegateContainer($yourContainer); /** * 4. Get the schema builder as normal. It will use the custom dependencies * you've defined. */ $schemaBuilder = $provider->getSchemaBuilder();
是否使用您的字段解析器使用其他 Phanua 依赖项完全取决于您,因为默认的 Phanua 实现是这样做的;您的字段解析器可以按您喜欢的任何方式工作。
另一种要考虑的方法是让您的字段解析器实现与 Phanua 使用的默认实现进行组合。通过这样做,您可以使用默认实现来处理与其兼容的字段,并使用您自己的实现来处理那些不兼容的字段。以下是一个示例。
<?php /** * 1. Define your field resolver implementation. */ use Cycle\Schema\Definition\Field; use Elazar\Phanua\Field\FieldResolverInterface; use Jane\Component\OpenApi3\JsonSchema\Model\Schema; class YourFieldResolver implements FieldResolverInterface { private FieldResolverInterface $fieldResolver; // Have the constructor accept another resolver implementation as a // parameter. public function __construct( FieldResolverInterface $fieldResolver, /* ... */ ) { $this->fieldResolver = $fieldResolver; /* ... */ } // Then, in the implementation of the interface method... public function getField( string $componentName, string $propertyName, Schema $propertySchema ): ?Field { // $field = ... // ... if your implementation can't resolve an entity... if ($field === null) { // ... then have it defer to the injected resolver. $field = $this->fieldResolver->getField( $componentName, $propertyName, $propertySchema ); } return $field; } } /** * 2. Inject your implementation to have Phanua use it. */ // Get the Phanua container and configure your container as normal... $phanuaContainer = $provider->getContainer(); $yourContainer = new \Pimple\Container; $yourContainer[FieldResolverInterface::class] = fn() => new YourFieldResolver( // ... then inject the field resolver implementation from the Phanua // container into your own implementation. $phanuaContainer[FieldResolverInterface::class] );
记录器
Phanua 支持任何 PSR-3 记录器。默认情况下,它使用 Psr\Logger\NullLogger
,该记录器会丢弃任何记录的条目。要存储这些条目,您必须用能够处理它们的记录器覆盖默认记录器。以下是一个使用 Pimple 容器覆盖默认记录器并使用来自 Monolog 库的记录器的示例。
<?php use Elazar\Phanua\Service\Provider; use Monolog\Logger; use Pimple\Container; use Psr\Log\LoggerInterface; $yourContainer = new Container; $yourContainer[LoggerInterface::class] = function () { $logger = new Logger; // Configure $logger as needed here return $logger; }; $provider = (new Provider) ->withDelegateContainer($yourContainer);
规范加载器
要将 OpenAPI 规范转换为 Cycle ORM 模式,Phanua 首先必须加载和解析该规范。
Phanua 定义了用于加载和解析 OpenAPI 规范文件的类的接口 Elazar\Phanua\Schema\SpecLoaderInterface
。
该接口包含一个名为 load()
的单个方法,该方法接收一个参数:一个包含 OpenAPI 规范文件路径的 string
。
load()
必须返回一个包含解析规范的 Jane\Component\OpenApi3\JsonSchema\Model\OpenApi
实例,或者在加载或解析规范失败时抛出一个 Elazar\Phanua\Schema\Exception
实例。
Phanua 默认使用的该接口实现是 Elazar\Phanua\Schema\SpecLoader
。它处理与加载和解析规范相关的日志事件。
要覆盖默认实现,请在您的容器中添加一个条目,键为 Elazar\Phanua\Schema\SpecLoaderInterface
,并将其解析为实现了该接口的类的实例。
注册表
当 Phanua 将 OpenAPI 组件转换为 Cycle ORM 实体时,它使用一个 Cycle\Schema\Registry
实例来注册实体并将它们链接到表。此注册表最终用于将实体编译成一个 Cycle\ORM\Schema
实例。
默认情况下,Phanua 使用为您的数据库配置的空 Registry
实例。如果您要将来自其他来源生成的实体编译到与 Phanua 生成的实体相同的模式中,则希望使用不同的实例。
在这种情况下,请使用键 Cycle\Schema\Registry
在您的容器中添加条目,并将其解析到您希望使用的 Registry
实例,以便 Phanua 能够使用它。请注意,您可以使用 Phanua 容器来使用其默认注册表的修改版本,或者使用与 Phanua 相同的 Spiral\Database\DatabaseManager
实例提供您注册表所需的 Spiral\Database\DatabaseProviderInterface
实现。以下是一个使用 Pimple 容器的示例。
<?php use Cycle\Schema\Registry; use Phanua\Service\Provider; use Pimple\Container; use Spiral\Database\DatabaseProviderInterface; $provider = new Provider; // If you're using Phanua\Service\Provider as a Pimple provider: $yourContainer = new Container; $yourContainer->register($provider); $yourContainer->extend(Registry::class, function (Registry $registry) { // Change $registry as needed here }); // If you're not using Phanua\Service\Provider as a Pimple provider: $phanuaContainer = $provider->getContainer(); $yourContainer = new Container; $yourContainer[Registry::class] = function () use ($phanuaContainer) { $databaseProvider = $phanuaContainer[DatabaseProviderInterface::class]; $registry = new Registry($databaseProvider); // Configure $registry here as needed return $registry; }; $phanuaContainer[Registry::class] = fn() => $yourContainer[Registry::class];
编译器配置
Phanua 使用 Cycle\Schema\Compiler
实例将实体注册表编译为模式。此编译器可以使用 自定义生成器 和 默认值。默认情况下,Phanua 使用这些的默认值(即空数组)。
如果您想为这两个中的任何一个使用自定义值,Phanua 提供了 Elazar\Phanua\Schema\CompilerConfiguration
来覆盖它们。将此类的条目添加到您的容器中,并使用配置的实例。以下是一个使用 Pimple 容器的示例。
<?php use Elazar\Phanua\Schema\CompilerConfiguration; use Elazar\Phanua\Service\Provider; use Pimple\Container; $yourContainer = new Container; $yourContainer[CompilerConfiguration::class] = fn() => (new CompilerConfiguration) ->withGenerators(/* ... */) ->withDefaults(/* ... */); $provider = (new Provider) ->withDelegateContainer($yourContainer);
常见问题解答
你为什么构建这个?
我已经在一些项目中使用 OpenAPI 和 Jane,并且也想使用 Cycle ORM。我已经使用了 Jane 生成的模型类,并不想手动注解或更改它们以与 Cycle 一起使用。我构建了这个,这样我就不需要这样做。
为什么叫做“Phanua”?
这个名字取自斯瓦希里语单词 kufafanua 的后半部分,意思是“定义”,并改编为用“ph”代替“f”,这是 PHP 项目的常见做法。
“Phanua”怎么发音?
FAH-NOO-ah。