Builds Cycle ORM schemas from OpenAPI 3 component schemas

Overview

Phanua

PHP Version Support Packagist Version Software License Buy Me a Cofee Patreon

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

  • PHP 7.4+
  • PDO and driver for desired database

Installation

Use Composer.

composer require elazar/phanua

Usage

<?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();

Configuration

For its most basic use, Phanua needs three parameters:

  1. the path to an OpenAPI 3 specification file in JSON or YAML format;
  2. a PHP namespace used by generated Jane model class files (without the Model subnamespace added by Jane); and
  3. 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.

<?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');

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.

<?php

$databaseConfig = [ /* ... */ ];
$provider = (new \Elazar\Phanua\Service\Provider)
    ->withDatabaseConfig($databaseConfig);

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.

<?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 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.

<?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(/* ... */)
);

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.

<?php

$provider = new \Elazar\Phanua\Service\Provider;

// Configure $provider as needed here.

$container = new \Pimple\Container;
$container->register($provider);

Once you've configured the Phanua service provider with the necessary parameters, it's possible to use the container it builds independently.

<?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);

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:

  1. a string containing the name of the OpenAPI component corresponding to the entity;
  2. an instance of Jane\Component\OpenApi3\JsonSchema\Model\Schema representing the component schema; and
  3. 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:

  1. a string containing the name of the OpenAPI component corresponding to the entity containing the field;
  2. a string containing the name of the component property corresponding to the field; and
  3. 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:

  1. a string containing the name of the OpenAPI component corresponding to the entity containing the field;
  2. a string containing the name of the component property corresponding to the field; and
  3. 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:

  1. a string containing the name of the OpenAPI component corresponding to the entity containing the field;
  2. a string containing the name of the component property corresponding to the field; and
  3. 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.

<?php

use Elazar\Phanua\Field\TypeResolver;
$container[TypeResolver::class] = new TypeResolver(
    null // fallback value goes here
);

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.

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:

  1. a string containing the name of the OpenAPI component corresponding to the entity containing the field;
  2. a string containing the name of the component property corresponding to the field; and
  3. 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:

  1. a string containing the name of the OpenAPI component corresponding to the entity; and
  2. 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 role and 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 list of components to exclude into EntityResolver.

<?php

$excludedComponents = [
    'component1',
    'component2',
    // ...
];

$provider = (new \Elazar\Phanua\Service\Provider)
    ->withExcludedComponents($excludedComponents);

You can also exclude components by overriding the EntityResolver instance used by default with one that includes the names of components to exclude in the $exclude constructor parameter of EntityResolver or does something similar for your own entity resolver implementation. The example below does this using a Pimple container.

<?php

use Elazar\Phanua\{
    Entity\ClassResolverInterface,
    Entity\EntityResolver,
    Entity\EntityResolverInterface,
    Entity\RoleResolverInterface,
    Service\Provider
};

$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],
    [ /* strings containing names of components to exclude go here */ ]
);

/**
 * 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();

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.

<?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]

);

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:

  1. a string containing the name of the OpenAPI component corresponding to the entity containing the field;
  2. a string containing the name of the component property corresponding to the field; and
  3. 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 column, type, and presence in the 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 list of properties to exclude into FieldResolver.

<?php

$excludedProperties = [
    'propertyName',
    'component.propertyName',
    // ...
];

$provider = (new \Elazar\Phanua\Service\Provider)
    ->withExcludedProperties($excludedProperties);

You can also exclude properties by overriding the FieldResolver instance used by default with one that includes the names of properties to exclude in the $exclude constructor parameter of FieldResolver. The example below does this using a Pimple container.

<?php

use Elazar\Phanua\{
    Field\ColumnResolverInterface,
    Field\FieldResolver,
    Field\FieldResolverInterface,
    Field\PrimaryResolverInterface,
    Field\TypeResolverInterface,
    Service\Provider
};

$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],
    [ // names of properties to exclude go here, e.g.
        'propertyName',
        'componentName.propertyName',
        // ...
    ]
);

/**
 * 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();

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.

<?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]

);

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.

<?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);

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. There may be instances where you want to use a different instances, such as wanting to compile entities generated from other sources into the same schema with entities generated by Phanua.

In such an instance, 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.

<?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];

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.

<?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);

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.

You might also like...
Articulate - An alternative ORM for Laravel, making use of the data mapper pattern

Articulate Laravel: 8.* PHP: 8.* License: MIT Author: Ollie Read Author Homepage: https://ollie.codes Articulate is an alternative ORM for Laravel bas

MongoDB ORM that includes support for references,embed and multilevel inheritance.
MongoDB ORM that includes support for references,embed and multilevel inheritance.

Introduction Features Requirements Installation Setup Database Basic Usage - CRUD Relationship - Reference Relationship - Embed Collection Inheritance

 Low code , Zero Configuration ORM that creates models, config, database and tables on the fly.
Low code , Zero Configuration ORM that creates models, config, database and tables on the fly.

🚀 ARCA ORM 🔥 Low code , Zero Configuration ORM that creates models, config, database and tables on the fly. 🔥 🇮🇳 Made in India 🇮🇳 Complete docu

The Enobrev\ORM library is a small framework of classes meant to be used for simply mapping a mysql database to PHP classes, and for creating simply SQL statements using those classes.

The Enobrev\ORM library is a small framework of classes meant to be used for simply mapping a mysql database to PHP classes, and for creating simply SQL statements using those classes.

Orm is a simple database abstraction layer that supports postgresql.

Orm What is it Orm is a simple database abstraction layer that supports postgresql. Welcome to join us or star us for encouragement. Requires php 8.1

[READ ONLY] Subtree split of the Illuminate Database component (see laravel/framework)

Illuminate Database The Illuminate Database component is a full database toolkit for PHP, providing an expressive query builder, ActiveRecord style OR

Process - The Process component executes commands in sub-processes.

Process Component The Process component executes commands in sub-processes. Sponsor The Process component for Symfony 5.4/6.0 is backed by SensioLabs.

OpenAPI(v3) Validators for Symfony http-foundation, using `league/openapi-psr7-validator` and `symfony/psr-http-message-bridge`.

openapi-http-foundation-validator OpenAPI(v3) Validators for Symfony http-foundation, using league/openapi-psr7-validator and symfony/psr-http-message

Generates OpenApi specification for Laravel, Lumen or Dingo using a configuration array and cebe/php-openapi

OpenApi Generator for Laravel, Lumen and Dingo. About The openapi-gen package provides a convenient way to create OpenApi specifications for Laravel,

Application de suivi de cycle pour les méthodes naturelles de régulation de naissance.

MONCYCLE.APP Application de suivi de cycle menstruel pour les méthodes naturelles de régulation de naissance. Plus d'information 👉 https://moncycle.a

Analogue ORM : Data Mapper ORM for Laravel/PHP

(this project is looking for a new maintainer) Analogue ORM Analogue is a flexible, easy-to-use ORM for PHP. It is a transposition of the Eloquent ORM

:white_check_mark: JoliCi - Run your TravisCi builds locally
:white_check_mark: JoliCi - Run your TravisCi builds locally

JoliCi JoliCi is a free and open source Continuous Integration Client written in PHP (5.4 minimum) and powered by Docker (please use a recent version)

Builds PHP so that multiple versions can be used side by side.

php-build php-build is a utility for building versions of PHP to use them side by side with each other. The overall structure is loosly borrowed from

Tarantool connector for yii2 framework. Allow to use activerecord, schemas, widgets and more.

Tarantool connector for yii2 framework Tarantool connector for yii2 framework. Allow to use framework abstractions such as ActiveRecord, Schema, Table

Builds nice, normalized and easy to consume REST JSON responses for Laravel powered APIs.

REST API Response Builder for Laravel Master branch: Development branch: Table of contents Introduction Why should I use it? Usage examples Features E

It validates PSR-7 messages (HTTP request/response) against OpenAPI specifications
It validates PSR-7 messages (HTTP request/response) against OpenAPI specifications

NOTICE - THE PACKAGE HAS BEEN CONTRIBUTED TO THE PHP LEAGUE Go to https://github.com/thephpleague/openapi-psr7-validator This package is here for exis

:tada: Release 2.0 is released! Very fast HTTP router for PHP 7.1+ (incl. PHP8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger)

HTTP router for PHP 7.1+ (incl. PHP 8 with attributes) based on PSR-7 and PSR-15 with support for annotations and OpenApi (Swagger) Installation compo

A simple API documentation package for Laravel using OpenAPI and Redoc

Laravel Redoc Easily publish your API documentation using your OpenAPI document in your Laravel Application. Installation You can install this package

A simple API documentation package for Laravel using OpenAPI and Stoplight Elements

Laravel Stoplight Elements Easily publish your API documentation using your OpenAPI document in your Laravel Application. Installation You can install

Owner
Matthew Turland
spouse, parent, egalitarian, geek, tenor, author, speaker, software engineer, open source enthusiast, idiopathic hypersomniac, he/him
Matthew Turland
Doctrine Object Relational Mapper (ORM)

3.0.x 2.9.x 2.8.x Doctrine 2 is an object-relational mapper (ORM) for PHP 7.1+ that provides transparent persistence for PHP objects. It sits on top o

Doctrine 9.5k Jan 2, 2023
Baum is an implementation of the Nested Set pattern for Laravel's Eloquent ORM.

Baum Baum is an implementation of the Nested Set pattern for Laravel 5's Eloquent ORM. For Laravel 4.2.x compatibility, check the 1.0.x branch branch

Estanislau Trepat 2.2k Jan 3, 2023
ORM layer that creates models, config and database on the fly

RedBeanPHP 5 RedBeanPHP is an easy to use ORM tool for PHP. Automatically creates tables and columns as you go No configuration, just fire and forget

Gabor de Mooij 2.2k Jan 9, 2023
Propel2 is an open-source high-performance Object-Relational Mapping (ORM) for modern PHP

Propel2 Propel2 is an open-source Object-Relational Mapping (ORM) for PHP. Requirements Propel uses the following Symfony Components: Config Console F

Propel 1.2k Dec 27, 2022
PHP DataMapper, ORM

Cycle ORM Cycle is PHP DataMapper, ORM and Data Modelling engine designed to safely work in classic and daemonized PHP applications (like RoadRunner).

Cycle ORM 1.1k Jan 8, 2023
A drop-in Doctrine ORM 2 implementation for Laravel 5+ and Lumen

Laravel Doctrine ORM A drop-in Doctrine ORM 2 implementation for Laravel 5+ $scientist = new Scientist( 'Albert', 'Einstein' ); $scientist->a

Laravel Doctrine 777 Dec 17, 2022
Extensions for the Eloquent ORM

Sofa/Eloquence Easy and flexible extensions for the Eloquent ORM. Currently available extensions: Searchable query - crazy-simple fulltext search thro

Jarek Tkaczyk 1.1k Dec 20, 2022
Ouzo Framework - PHP MVC ORM

Ouzo is a PHP MVC framework with built-in ORM and util libraries. PHP 8.0 or later is required. We believe in clean code and simplicity. We value unit

Ouzo 69 Dec 27, 2022
Simple Enum cast for Eloquent ORM using myclabs/php-enum.

Enum cast for Eloquent Simple Enum cast for Eloquent ORM using myclabs/php-enum. Requirements PHP 7.3 or higher Laravel 8.0 or higher Installation You

Orkhan Ahmadov 5 Apr 21, 2022
Extra RedBean ORM

RedBeanPHP 5 RedBeanPHP is an easy to use ORM tool for PHP. Automatically creates tables and columns as you go No configuration, just fire and forget

GingTeam Development 5 Nov 23, 2022