项目作者: cebe

项目描述 :
REST API application generator for Yii2, openapi 3.0 YAML -> Yii2
高级语言: PHP
项目地址: git://github.com/cebe/yii2-openapi.git
创建时间: 2018-10-23T10:39:18Z
项目社区:https://github.com/cebe/yii2-openapi

开源协议:MIT License

下载


yii2-openapi

>

This repository has been moved to https://github.com/php-openapi/yii2-openapi.
Please use the new package php-openapi/yii2-openapi instead.

REST API application generator for Yii2, openapi 3.0 YAML -> Yii2.

Base on Gii, the Yii Framework Code Generator.

Latest Stable Version
Latest Alpha Version
Total Downloads
License
yii2-openapi

TLDR; what is this?

A code generator for OpenAPI and Yii Framework based PHP API application.

Input: OpenAPI 3.0 YAML or JSON (via cebe/php-openapi)

Output: Yii Framework Application with Controllers, Models, database schema

Features

Currently available features:

  • Generate Path mappings, Controllers and Actions for API Endpoints. CRUD Endpoints are ready-to-use, other Endpoints are generated as abstract functions that need to be implemented
  • Generate Models and validation based on OpenAPI Schema
  • Generate Database Schema from OpenAPI Schema
  • Generates Database Migrations for schema changes
  • Provide Dummy Data via Faker for development

Requirements

  • PHP 7.1 or higher (works fine with PHP 8)

Install

  1. composer require php-openapi/yii2-openapi:^2.0@beta

Usage

You can use this package in your existing application or start a new project using the
yii2-app-api application template.
For usage of the template, see instructions in the template repo readme.

In your existing Yii application config (works for console as well as web):

  1. <?php
  2. $config = [
  3. // ... this is your application config ...
  4. ];
  5. if (YII_ENV_DEV) {
  6. // enable Gii module
  7. $config['bootstrap'][] = 'gii';
  8. $config['modules']['gii'] = [
  9. 'class' => \yii\gii\Module::class,
  10. 'generators' => [
  11. // add ApiGenerator to Gii module
  12. 'api' => \cebe\yii2openapi\generator\ApiGenerator::class,
  13. // --------- OR ---------
  14. // to disable generation of migrations files or with default config change
  15. 'api' => [
  16. 'class' => \cebe\yii2openapi\generator\ApiGenerator::class,
  17. 'generateMigrations' => false, # this config can also be applied in CLI command
  18. ],
  19. ],
  20. ];
  21. }
  22. return $config;

To use the web generator, open index.php?r=gii and select the REST API Generator.

On console you can run the generator with ./yii gii/api --openApiPath=@app/openapi.yaml. Where @app/openapi.yaml should be the absolute path to your OpenAPI spec file. This can be JSON as well as YAML (see also cebe/php-openapi for supported formats).

Run ./yii gii/api --help for all options. Example: Disable generation of migrations files ./yii gii/api --generateMigrations=0

See Petstore example for example OpenAPI spec.

OpenAPI extensions

This library understands the following extensions to the OpenAPI spec:

x-faker

You may specify custom PHP code for generating fake data for a property:

  1. Post:
  2. properties:
  3. id:
  4. type: integer
  5. tags:
  6. type: array
  7. items:
  8. type: string
  9. example: ['one', 'two']
  10. x-faker: "$faker->randomElements(['one', 'two', 'three', 'four'])"

To avoid generating faker code for particular model attribute, use value false:

  1. Post:
  2. properties:
  3. age:
  4. type: integer
  5. x-faker: false

Using in reference with allOf:

  1. Invoice:
  2. type: object
  3. required:
  4. - id
  5. properties:
  6. id:
  7. type: integer
  8. Order:
  9. type: object
  10. required:
  11. - id
  12. properties:
  13. id:
  14. type: integer
  15. invoice:
  16. allOf:
  17. - $ref: '#/components/schemas/Invoice'
  18. - x-faker: false

x-table

Specify the table name for a Schema that defines a model which is stored in the database.
You can generate non-db model based on \yii\base\Model without migrations by setting x-table: false

x-pk

Explicitly specify primary key name for table, if it is different from “id”

  1. Post:
  2. x-table: posts
  3. x-pk: uid
  4. properties:
  5. uid:
  6. type: integer
  7. title:
  8. type: string

x-db-type

Explicitly specify the database type for a column. (MUST contain only real DB type! (json, jsonb, uuid, varchar etc.)).
If x-db-type is set to false, property will be processed as virtual;
It will be added in model as public property, but skipped for migrations generation.

Example values of x-db-type are:

  • false (boolean false)
  • as string and its value can be like:
    • text
    • text[]
    • INTEGER PRIMARY KEY AUTO_INCREMENT
    • decimal(12,4)
    • json
    • varchar
    • VARCHAR
    • SMALLINT UNSIGNED ZEROFILL
    • MEDIUMINT(10) UNSIGNED ZEROFILL COMMENT “comment” (note the double quotes here)

Such values are not allowed:

  • int null default null after low_price (null and default will be handled by nullable and default keys respectively)
  • MEDIUMINT(10) UNSIGNED ZEROFILL NULL DEFAULT ‘7’ COMMENT ‘comment’ AFTER seti, ADD INDEX t (w)

If enum and x-db-type both are provided then for database column schema (migrations), only x-db-type will be considered ignoring enum.

x-indexes

Specify table indexes

  1. Post:
  2. x-table: posts
  3. x-indexes:
  4. - 'visible,publish_date'
  5. - 'unique:title' #for unique attributes also unique validation check will be added
  6. - 'gist:metadata' #for postgres will generate index using GIST index type
  7. properties:
  8. id:
  9. type: integer
  10. x-db-type: INTEGER PRIMARY KEY AUTO_INCREMENT
  11. title:
  12. type: string
  13. visible:
  14. type: boolean
  15. publish_date:
  16. type: string
  17. format: date
  18. metadata:
  19. type: object
  20. x-db-type: JSON
  21. default: '{}'

x-db-default-expression

Ability to provide default value by database expression

  1. created_at:
  2. readOnly: true
  3. type: string
  4. format: datetime
  5. x-db-type: datetime
  6. nullable: false
  7. x-db-default-expression: current_timestamp()

Note: If both default and x-db-default-expression are present then default will be considered.

  1. created_at:
  2. readOnly: true
  3. type: string
  4. format: datetime
  5. x-db-type: datetime
  6. nullable: false
  7. x-db-default-expression: current_timestamp() # this will be ignored
  8. default: "2011-11-11" # this will be considered

Also see: https://dev.mysql.com/doc/refman/8.0/en/data-type-defaults.html

x-fk-on-delete

Allow to set foreign key constraint in migrations for ON DELETE event of row in database table. Example:

  1. components:
  2. schemas:
  3. User:
  4. type: object
  5. description: x on-x (update|delete) foreign key constraint
  6. properties:
  7. id:
  8. type: integer
  9. name:
  10. type: string
  11. Post:
  12. type: object
  13. description: x on-x (update|delete) foreign key constraint
  14. properties:
  15. id:
  16. type: integer
  17. title:
  18. type: string
  19. user:
  20. allOf:
  21. - $ref: '#/components/schemas/User'
  22. - x-fk-on-update: CASCADE
  23. user_2:
  24. allOf:
  25. - $ref: '#/components/schemas/User'
  26. - x-fk-on-update: CASCADE
  27. - x-fk-on-delete: SET NULL
  28. user_3:
  29. allOf:
  30. - $ref: '#/components/schemas/User'
  31. - x-fk-on-delete: SET NULL
  32. user_4:
  33. $ref: '#/components/schemas/User' # without any constraints

x-fk-on-update

Allow to set foreign key constraint in migrations for ON UPDATE event of row in database table. For example, see above section for x-fk-on-delete.

x-fk-column-name

Provide custom database table column name in case of relationship column. This will not reflect in models relations, faker etc. Example:

  1. components:
  2. schemas:
  3. Webhook:
  4. type: object
  5. description: example for x-fk-column-name
  6. properties:
  7. id:
  8. type: integer
  9. name:
  10. type: string
  11. user:
  12. $ref: '../openapi.yaml#/components/schemas/User' # this will automatically create `user_id` column
  13. redelivery_of:
  14. allOf:
  15. - $ref: '../openapi.yaml#/components/schemas/Delivery'
  16. # this will automatically create `redelivery_of_id` column, but to avoid that use below extension
  17. - x-fk-column-name: redelivery_of # this will create `redelivery_of` column instead of `redelivery_of_id`

Many-to-Many relation definition

There are two ways for define many-to-many relations:

Simple many-to-many without junction model

  • property name for many-to-many relation should be equal lower-cased, pluralized related schema name

  • referenced schema should contains mirrored reference to current schema

  • migration for junction table can be generated automatically - table name should be [pluralized, lower-cased
    schema_name1]2[pluralized, lower-cased schema name2], in alphabetical order;
    For example, for schemas Post and Tag - table should be posts2tags, for schemas Post and Attachement - table should
    be attachments2posts

  1. Post:
  2. properties:
  3. ...
  4. tags:
  5. type: array
  6. items:
  7. $ref: '#/components/schemas/Tag'
  8. Tag:
  9. properties:
  10. ...
  11. posts:
  12. type: array
  13. items:
  14. $ref: '#/components/schemas/Post'

Many-to-many with junction model

This way allowed creating multiple many-to-many relations between to models

  • define junction schema with all necessary attributes. There are only one important requirement - the junction
    schema name
    must be started with prefix ‘junction_’ (This prefix will be used internally only and
    will be trimmed before table and model generation)
  1. # Model TeamMembers with table team_members will be generated with columns team_id, user_id and role
  2. junction_TeamMembers:
  3. team:
  4. $ref: '#/components/schemas/Team'
  5. user:
  6. $ref: '#/components/schemas/User'
  7. role:
  8. type: string
  • Both many-to-many related schemas must have properties with reference to “junction_*” schema. These properties will be
    used as relation names
  1. Team:
  2. properties:
  3. ...
  4. team_members:
  5. type: array
  6. items:
  7. $ref: '#/components/schemas/junction_TeamMembers'
  8. User:
  9. properties:
  10. ...
  11. memberships: #You absolutely free with naming for relationship attributes
  12. type: array
  13. items:
  14. $ref: '#/components/schemas/junction_TeamMembers'

Handling of NOT NULL constraints

NOT NULL in DB migrations is determined by nullable and required properties of the OpenAPI schema.
e.g. attribute = ‘my_property’.

  • If you define attribute neither “required” nor via “nullable”, then it is by default NULL:
  1. ExampleSchema:
  2. properties:
  3. my_property:
  4. type: string
  • If you define attribute in “required”, then it is NOT NULL
  1. ExampleSchema:
  2. required:
  3. - my_property
  4. properties:
  5. my_property:
  6. type: string
  • If you define attribute via “nullable”, then it overrides “required”, e.g. allow NULL in this case:
  1. ExampleSchema:
  2. required:
  3. - my_property
  4. properties:
  5. my_property:
  6. type: string
  7. nullable: true
  • If you define attribute via “nullable”, then it overrides “required”, e.g. NOT NULL in this case:
  1. test_table:
  2. required:
  3. properties:
  4. my_property:
  5. type: string
  6. nullable: false

Handling of enum (#enum)

It works on all 3 DB: MySQL, MariaDb and PgSQL.

  1. test_table:
  2. properties:
  3. my_property:
  4. enum:
  5. - one
  6. - two
  7. - three

Note: Changes in enum values are not very simple. For Mysql and Mariadb, migrations will be generated but in many cases custom modification in it are required. For Pgsql migrations for change in enum values will not be generated. It should be handled manually.

It will be ignored for database column schema (migrations) if x-db-type is provided.

Handling of numeric (#numeric, #MariaDb)

precision-default = 10
scale-default = 2

  • You can define attribute like “numeric(precision,scale)”:

    1. test_table:
    2. properties:
    3. my_property:
    4. x-db-type: decimal(12,4)

    DB-Result = decimal(12,4)

  • You can define attribute like “numeric(precision)” with default scale-default = 2:

    1. test_table:
    2. properties:
    3. my_property:
    4. x-db-type: decimal(12)

    DB-Result = decimal(12,2)

  • You can define attribute like “numeric” with precision-default = 10 and scale-default = 2:

    1. test_table:
    2. properties:
    3. my_property:
    4. x-db-type: decimal

    DB-Result = decimal(10,2)

Handling of timestamp database column data type

If field is defined as

  1. created_at:
  2. type: string
  3. format: date-time # or datetime
  4. example: '2020-03-14T21:42:17Z'
  5. readOnly: true

then database type selected will be timestamp. This is by design. If datetime data type is needed, use x-db-type as

  1. created_at:
  2. type: string
  3. format: date-time # or datetime
  4. example: '2020-03-14T21:42:17Z'
  5. x-db-type: datetime
  6. readOnly: true

Assumptions

When generating code from an OpenAPI description there are many possible ways to achive a fitting result.
Thus there are some assumptions and limitations that are currently applied to make this work.
Here is a (possibly incomplete) list:

  • The current implementation works best with OpenAPI description that follows the JSON:API guidelines.
    • The request and response format/schema is currently not extracted from OpenAPI schema and may need to be adjusted manually if it does not follow JSON:API
  • column/field/property with name id is considered as Primary Key by this library and it is automatically handled by DB/Yii; so remove it from validation rules()
    • other fields can currently be used as primary keys using the x-pk OpenAPI extension (see below) but it may not be work correctly in all cases, please report bugs if you find them.

Other things to keep in mind:

Adding columns to existing tables

When adding new fields in the API models, new migrations will be generated to add these fields to the table.
For a project that is already in production, it should be considered to adjust the generated migration to add default
values for existing data records.

One case where this is important is the addition of a new column with NOT NULL contraint, which does not provide a default value.
Such a migration will fail when the table is not empty:

  1. $this->addColumn('{{%company}}', 'name', $this->string(128)->notNull());

Fails on a PostgreSQL database with

add column name string(128) NOT NULL to table {{%company}} …Exception: SQLSTATE[23502]: Not null violation: 7 ERROR: column “name” contains null values

The solution would be to create the column, allowing NULL, set the value to a default and add the null constraint later.

  1. $this->addColumn('{{%company}}', 'name', $this->string(128)->null());
  2. $this->update('{{%company}}', ['name' => 'No name']);
  3. $this->alterColumn('{{%company}}', 'name', $this->string(128)->notNull());

Screenshots

Gii Generator Form:

Gii Generator Form

Generated files:

Gii Generated Files

Development

To contribute or play around, steps to set up this project up locally are in CONTRIBUTING.md.

Support

Need help with your API project?

Professional support, consulting as well as software development services are available:

https://www.cebe.cc/en/contact

Development of this library is sponsored by cebe.:cloud: “Your Professional Deployment Platform”.