Download the PHP package elazar/phanua without Composer
On this page you can find all versions of the php package elazar/phanua. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download elazar/phanua
More information about elazar/phanua
Files in elazar/phanua
Package phanua
Short Description Builds Cycle ORM schemas from OpenAPI 3 component schemas
License MIT
Informations about the package phanua
Phanua
OpenAPI 3 + Jane + Cycle ORM = 🔥
Phanua builds Cycle ORM schemas from OpenAPI 3 component schemas.
Released under the MIT License.
WARNING: This project is in an alpha state of development and may be subject to changes that break backward compatibility. It may contain bugs, lack features or extension points, or be otherwise unsuitable for production use. User discretion is advised.
Requirements
Installation
Use Composer.
Usage
Configuration
For its most basic use, Phanua needs three parameters:
- the path to an OpenAPI 3 specification file in JSON or YAML format;
- a PHP namespace used by generated Jane model class files (without the
Model
subnamespace added by Jane); and - a Cycle ORM database configuration.
You can pass these parameters to Phanua by using an instance of the Phanua service provider class Elazar\Phanua\Service\Provider
.
You can provide the path and namespace provided using your Jane configuration file or by passing them directly to the service provider instance.
You can provide the database configuration by specifying the same array passed to Spiral\Database\Config\DatabaseConfig
. See related Cycle documentation for an example of this array.
Providing the database configuration in other ways requires an explanation of how Phanua handles its dependencies.
Overriding Dependencies
To overriding a dependency of Phanua, you must provide a dependency injection container that includes that dependency.
Phanua can use any container that implements the PSR-11 standard. An example of such a container is Pimple, which Phanua uses internally. Pimple is the recommended container to use if you aren't already using a different one.
Phanua expects this container to use fully-qualified class names as entry identifiers. Below are examples of alternate methods of configuring Pimple to supply the database configuration.
If you're already using Pimple and want Phanua to use dependencies you've registered in your container, you can register your configured Phanua service provider instance with your container as a provider.
Once you've configured the Phanua service provider with the necessary parameters, it's possible to use the container it builds independently.
Role Resolver
Cycle ORM uses the term "role" to refer to a unique name for an entity.
Phanua defines the interface Elazar\Phanua\Entity\RoleResolverInterface
for classes used to determine the role for a given entity.
This interface contains a single method getRole()
which receives a single parameter: a string
containing the name of the OpenAPI component corresponding to the entity.
getRole()
must return a string
containing the determined role for the entity.
The implementation of this interface that Phanua uses by default is Elazar\Phanua\Entity\RoleResolver
. Its getRole()
method returns the OpenAPI component name it receives so that the component name and role are the same.
To override the default implementation, add an entry to your container using the key Elazar\Phanua\Entity\RoleResolverInterface
and have it resolve to an instance of a class that implements the interface.
Class Resolver
Entities in Cycle ORM schemas have corresponding classes.
Phanua defines the interface Elazar\Phanua\Entity\ClassResolverInterface
for classes used to determine the class for a given entity.
This interface contains a single method, getClass()
, which receives a single parameter: a string
containing the name of the OpenAPI component corresponding to the entity.
getClass()
must return a string
containing the fully-qualified name of the class for the entity.
The implementation of this interface that Phanua uses by default is Elazar\Phanua\Entity\ClassResolver
. Its getClass()
method returns a class name constructed with the namespace used by Jane from the Phanua service provider configuration and the OpenAPI component name it receives.
To override the default implementation, add an entry to your container using the key Elazar\Phanua\Entity\ClassResolverInterface
and have it resolve to an instance of a class that implements the interface.
Table Resolver
Each Cycle ORM entity has a corresponding table.
Phanua defines the interface Elazar\Phanua\Entity\TableResolverInterface
for classes used to determine the table associated with a given entity.
This interface contains a single method, getTable()
, which receives three parameters:
- a
string
containing the name of the OpenAPI component corresponding to the entity; - an instance of
Jane\Component\OpenApi3\JsonSchema\Model\Schema
representing the component schema; and - an instance of
Cycle\Schema\Definition\Entity
representing the entity.
getTable()
must return a string
containing the name of the table for the entity.
The implementation of this interface that Phanua uses by default is Elazar\Phanua\Entity\TableResolver
. Its getTable()
method returns the entity role, as determined by the instance of Elazar\Phanua\Entity\RoleResolverInterface
in use, so that the role and table name are the same. See the "Role Resolver" section above for further details.
To override the default implementation, add an entry to your container using the key Elazar\Phanua\Entity\TableResolverInterface
and have it resolve to an instance of a class that implements the interface.
Name Resolver
Entities in Cycle ORM contain fields that have names.
Phanua defines the interface Elazar\Phanua\Field\NameResolverInterface
for classes used to determine the name of a field.
This interface contains a single method, getName()
, which receives three parameters:
- a
string
containing the name of the OpenAPI component corresponding to the entity containing the field; - a
string
containing the name of the component property corresponding to the field; and - an instance of
Jane\Component\OpenApi3\JsonSchema\Model\Schema
representing the property schema.
getName()
must return a string
containing the name to assign to the field.
The implementation of this interface that Phanua uses by default is Elazar\Phanua\Field\NameResolver
. Its getName()
method returns the OpenAPI property name it receives so that the property name and field name are the same.
To override the default implementation, add an entry to your container using the key Elazar\Phanua\Field\NameResolverInterface
and have it resolve to an instance of a class that implements the interface.
Column Resolver
Cycle ORM entity fields have corresponding table columns.
Phanua defines the interface Elazar\Phanua\Field\ColumnResolverInterface
for classes used to determine the name of the column for a field.
This interface contains a single method, getColumn()
, which receives three parameters:
- a
string
containing the name of the OpenAPI component corresponding to the entity containing the field; - a
string
containing the name of the component property corresponding to the field; and - an instance of
Jane\Component\OpenApi3\JsonSchema\Model\Schema
representing the property schema.
getColumn()
must return a string
containing the name to assign to the column.
The implementation of this interface that Phanua uses by default is Elazar\Phanua\Field\ColumnResolver
. Its getColumn()
method returns the OpenAPI property name it receives so that the property name and column name are the same.
To override the default implementation, add an entry to your container using the key Elazar\Phanua\Field\ColumnResolverInterface
and have it resolve to an instance of a class that implements the interface.
Type Resolver
Each Cycle ORM entity field has a corresponding abstract type.
Phanua defines the interface Elazar\Phanua\Field\TypeResolverInterface
for classes used to determine the type for a field.
This interface contains a single method, getType()
, which receives three parameters:
- a
string
containing the name of the OpenAPI component corresponding to the entity containing the field; - a
string
containing the name of the component property corresponding to the field; and - an instance of
Jane\Component\OpenApi3\JsonSchema\Model\Schema
representing the property schema.
getType()
may return a string
containing the type to assign to the column or null
if it's unable to resolve the column to a type.
The implementation of this interface that Phanua uses by default is Elazar\Phanua\Field\TypeResolver
. Its getType()
method returns a type it derives from the property type and format as well as validation constraints on the property value (e.g. minimum
, exclusiveMinimum
, maximum
, or exclusiveMaximum
for numeric values, minLength
and maxLength
for string / text values). Lacking these constraints, getType()
returns the largest available type to allow for maximal possible values.
NOTE: This implementation does not handle nested objects, arrays, or references to other component schemas. This may change in the future.
In the event of being unable to resolve to a more specific type, getType()
supports falling back to a specified type. To reduce configuration needed for common use cases, the default value for this fallback type is 'string'
.
You may want change this fallback type if, for example, you want to compose TypeResolver
with your own resolver implementation to resolve types that TypeResolver
doesn't cover. In that case, you would set the fallback type to null
so that your resolver knows when TypeResolver
can't resolve a given type. Below is an example of overriding the fallback type using a Pimple container.
To override the default type resolver implementation with your own, add an entry to your container using the key Elazar\Phanua\Field\TypeResolverInterface
and have it resolve to an instance of a class that implements the interface.
If you want to use the default type resolver, your own resolver, or another resolver together, you can compose them using Elazar\Phanua\Field\CompositeFieldResolver
, which will use resolvers in turn until one returns a type.
Primary Resolver
Each table must have a primary index on one or more columns for a compiled Cycle ORM schema to include it.
This circumstance is a limitation of the compiler that handles compiling entities from the registry into a schema: it skips entities without a primary index.
To draw attention to this behavior, Phanua throws an exception if primary index resolution fails for a given entity. To avoid this, you must explicitly exclude any entities in your OpenAPI specification that you do not want to include in the generated Cycle ORM schema. The next section discusses this further.
Phanua defines the interface Elazar\Phanua\Field\PrimaryResolverInterface
for classes used to determine whether a given field should be part of the primary index of the table containing its respective column.
This interface contains a single method, isPrimary()
, which receives three parameters:
- a
string
containing the name of the OpenAPI component corresponding to the entity containing the field; - a
string
containing the name of the component property corresponding to the field; and - an instance of
Jane\Component\OpenApi3\JsonSchema\Model\Schema
representing the property schema.
isPrimary()
must return a boolean
value where a value of true
indicates that the given field is part of the primary index for the associated table and a value of false
indicates that it's not.
The implementation of this interface that Phanua uses by default is Elazar\Phanua\Field\PrimaryResolver
. Its isPrimary()
method returns true
if the given property name is id
or false
otherwise. This class assumes that the property and column names are the same, which is the case when using the default implementation of Elazar\Phanua\Field\ColumnResolverInterface
detailed in the "Column Resolver" section above.
If your property and column names are not consistent with each other, or if any of your tables have a primary index that includes more than one column, create your own implementation of this interface to suit the needs of your database.
To override the default implementation, add an entry to your container using the key Elazar\Phanua\Field\PrimaryResolverInterface
and have it resolve to an instance of a class that implements the interface.
Entity Resolver
Phanua represents the process of converting an OpenAPI component to a Cycle ORM entity with the interface Elazar\Phanua\Entity\EntityResolverInterface
.
This interface contains a single method, getEntity()
, which receives two parameters:
- a
string
containing the name of the OpenAPI component corresponding to the entity; and - an instance of
Jane\Component\OpenApi3\JsonSchema\Model\Schema
representing the component schema.
getEntity()
must return a populated instance of Cycle\Schema\Definition\Entity
representing the entity corresponding to the given component or null
if it fails to resolve the component to an entity.
The implementation of this interface that Phanua uses by default is Elazar\Phanua\Entity\EntityResolver
. It composes an implementation of each of Elazar\Phanua\Entity\RoleResolverInterface
and Elazar\Phanua\Entity\ClassResolverInterface
, which it uses to determine the entity class respectively.
It also provides for the exclusion of specific components from having an entity in the generated schema, such as those components where the corresponding entity would not have a primary index. The easiest way to exclude one or more components is by using the service provider: by default, it handles injecting a callback to filter components into EntityResolver
.
You can also exclude components by overriding the EntityResolver
instance used by default with one that includes a callback to filter entities in the $filterCallback
constructor parameter of EntityResolver
or does something similar for your own entity resolver implementation. The example below does this using a Pimple container.
It's entirely optional to have your entity resolver use other Phanua dependencies, as the default Phanua implementation does; your entity resolver can function in whatever way you like.
Another approach to consider is having your entity resolver implementation compose the default implementation that Phanua uses. By doing so, you can use the default implementation for entities that are compatible with it and your own implementation for those that are not. See the example of this below.
Field Resolver
Phanua includes a resolver for fields as it does for entities, represented by the Elazar\Phanua\Field\FieldResolverInterface
.
This interface contains a single method, getField()
, which receives three parameters:
- a
string
containing the name of the OpenAPI component corresponding to the entity containing the field; - a
string
containing the name of the component property corresponding to the field; and - an instance of
Jane\Component\OpenApi3\JsonSchema\Model\Schema
representing the property schema.
getField()
must return a populated instance of Cycle\Schema\Definition\Field
representing the field corresponding to the given property or null
if it fails to resolve the component to a field.
The implementation of this interface that Phanua uses by default is Elazar\Phanua\Field\FieldResolver
. It composes an implementation of each of Elazar\Phanua\Field\ColumnResolverInterface
, Elazar\Phanua\Field\TypeResolverInterface
, and Elazar\Phanua\Field\PrimaryResolverInterface
, which it uses to determine the field primary index respectively.
It also handles setting other options for the field, such as its default value and whether it's nullable based on corresponding default
and nullable
values from the given property schema.
FieldResolver
also provides for the exclusion of specific properties from having a field in the generated schema. The easiest way to exclude one or more properties is by using the service provider: by default, it handles injecting a callback to filte properties into FieldResolver
.
You can also exclude properties by overriding the FieldResolver
instance used by default with one that includes a callback to filter properties in the $filterCallback
constructor parameter of FieldResolver
. The example below does this using a Pimple container.
It's entirely optional to have your field resolver use other Phanua dependencies, as the default Phanua implementation does; your field resolver can function in whatever way you like.
Another approach to consider is having your field resolver implementation compose the default implementation that Phanua uses. By doing so, you can use the default implementation for fields that are compatible with it and your own implementation for those that are not. See the example of this below.
Logger
Phanua supports any PSR-3 logger. By default, it uses Psr\Logger\NullLogger
, which discards any logged entries. To store these entries, you must override the default logger with one that does something with them. Below is an example of using the Pimple container to override the default logger with one from the Monolog library.
Specification Loader
To convert an OpenAPI specification to a Cycle ORM schema, Phanua must first load and parse that specification.
Phanua defines the interface Elazar\Phanua\Schema\SpecLoaderInterface
for classes used to load and parse OpenAPI specification files.
This interface contains a single method load()
which receives a single parameter: a string
containing the path to an OpenAPI specification file.
load()
must return an instance of Jane\Component\OpenApi3\JsonSchema\Model\OpenApi
containing the parsed specification or throw an instance of Elazar\Phanua\Schema\Exception
if loading or parsing the specification fails.
The implementation of this interface that Phanua uses by default is Elazar\Phanua\Schema\SpecLoader
. It handles logging events related to loading and parsing the specification.
To override the default implementation, add an entry to your container using the key Elazar\Phanua\Schema\SpecLoaderInterface
and have it resolve to an instance of a class that implements the interface.
Registry
As Phanua converts OpenAPI components to Cycle ORM entities, it uses an instance of Cycle\Schema\Registry
to register the entities and link them to tables. This registry is ultimately used to compile the entities into an instance of Cycle\ORM\Schema
.
By default, Phanua uses an empty Registry
instance configured for your database. You want to use a different instance if, for example, you're compiling entities generated from other sources into the same schema with entities generated by Phanua.
In such situations, add an entry to your container using the key Cycle\Schema\Registry
and have it resolve to your desired Registry
instance to have Phanua use it. Note that you can use the Phanua container to either use a modified version of its default registry instance or to supply your registry's required implementation of Spiral\Database\DatabaseProviderInterface
using the same instance of Spiral\Database\DatabaseManager
that Phanua does. Below is an example of doing each with a Pimple container.
Compiler Configuration
Phanua uses an instance of Cycle\Schema\Compiler
to compile a registry of entities into a schema. This compiler can use custom generators and defaults. By default, Phanua uses default values for each of these (i.e. empty arrays).
If you want to use custom values for either of these, Phanua provides Elazar\Phanua\Schema\CompilerConfiguration
to override them. Add an entry for this class to your container with a configured instance. Below is an example of doing this with a Pimple container.
FAQ
Why did you build this?
I was already using OpenAPI and Jane in some projects and wanted to use Cycle ORM as well. I was already using model classes generated by Jane and didn't want to have to manually annotate or otherwise change them to be usable with Cycle. I built this so I wouldn't have to.
Why the name "Phanua?"
It's taken from the latter half of the Swahili word kufafanua, which means "to define," and adapted to replace the "f" with "ph" as is common with PHP project names.
How do you pronounce "Phanua?"
Fah-NOO-ah.
All versions of phanua with dependencies
cycle/orm Version ^1.5
cycle/schema-builder Version ^1.2
jane-php/open-api-3 Version ^7.1
pimple/pimple Version ^3.4
psr/container Version ^1
psr/log Version ^1.1