Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
chriskapp committed Dec 31, 2024
1 parent b29a335 commit 0618f70
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 161 deletions.
31 changes: 8 additions & 23 deletions docs/components/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@ sidebar_position: 2

# API

The API component is the reference implementation of the [TypeAPI](https://typeapi.org/) specification.
It provides models to describe an REST API and generate based on those models different outputs. You can create those
models either by parsing a TypeAPI or OpenAPI specification or by using PHP Attributes. Based on those models it is then
possible to generate i.e. client SDKs.
This library provides an attribute parser to dynamically generate a [TypeAPI](https://typeapi.org/) specification
from any controller (code-first). Based on the specification it is then possible to generate client SDKs or
an OpenAPI specification.

We provide also a hosted version of this [code generator](https://typeapi.org/generator).
For more integration options you can also take a look at the [SDKgen](https://sdkgen.app/) project
which provides a CLI binary or GitHub action to integrate the code generator.

## Usage

Expand Down Expand Up @@ -42,7 +45,7 @@ class MyController

```

This would be then enough for the API component to generate either an OpenAPI specification or a client SDK.
This would be enough for the API component to generate either an OpenAPI specification or a client SDK.
Note this library only needs the meta information, if you can get those meta information at your framework in another
way you can also implement a custom `ParserInterface`.

Expand Down Expand Up @@ -88,21 +91,3 @@ $generator = $registry->getGenerator(\PSX\Api\Repository\LocalRepository::CLIENT
$source = $generator->generate($resource);

```

## Generator

### Client

- PHP
- Typescript

### Markup

- Client
- HTML
- Markdown

### Spec

- [OpenAPI](https://github.com/OAI/OpenAPI-Specification)
- [TypeAPI](https://typeapi.org/)
26 changes: 0 additions & 26 deletions docs/components/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,32 +138,6 @@ The writer factory can also be extended with custom writer implementations.
$configuration->getWriterFactory()->addWriter(new Acme\Writer(), 64);
```

## Constraints

It is also possible to add specific constraints to your model class. In the following some examples:

```php
#[Required(['title'])]
class News
{
#[Pattern('[A-z]')]
private ?string $title = null;

#[MinLength(3)]
#[MaxLength(255)]
private ?string $text = null;

#[Enum(['active', 'deleted'])]
private ?string $status = null;

#[Minimum(0)]
#[Maximum(5)]
private ?int $rating = null;
}
```

All available attributes are located at the [psx/schema](https://github.com/apioo/psx-schema) project.

## Transformations

Each reader class returns the data in a form which can be easily processed. I.e.
Expand Down
238 changes: 126 additions & 112 deletions docs/components/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,93 @@ sidebar_position: 3

# Schema

This library can parse a [TypeSchema](https://typeschema.org/) specification either from a JSON file or from PHP
classes using reflection and attributes. Based on this schema it can generate source code and transform raw JSON data
into DTO objects. Through this you can work with fully typed objects in your API for incoming and outgoing data. It
provides basically the following features:
This library helps you to work with fully typed objects, it provides the following features:

* Transform raw JSON data into DTO objects
* Generate source code based on a schema (i.e. PHP, Typescript)
* Validate data according to the provided schema
* Transform raw JSON data into fully typed objects
* Parse PHP classes into a [TypeSchema](https://typeschema.org/) specification
* Generate DTOs in different languages like TypeScript, Java, C# etc.

## Usage
We provide also a hosted version of this [code generator](https://typeschema.org/generator).
For more integration options you can also take a look at the [SDKgen](https://sdkgen.app/) project
which provides a CLI binary or GitHub action to integrate the code generator.

At first, we need to describe our data format with a [TypeSchema](https://typeschema.org/) specification. Then we can
generate based on this specification the fitting PHP classes.
## Object mapper

This example reads raw JSON data and transform it into the provided `Person` class.

```php
$json = <<<'JSON'
{
"firstName": "Ludwig",
"lastName": "Beethoven",
"age": 254
}
JSON;

$objectMapper = new ObjectMapper(new SchemaManager());

$person = $objectMapper->readJson($json, SchemaSource::fromClass(Person::class));

assert('Ludwig' === $person->getFirstName());
assert('Beethoven' === $person->getLastName());
assert(254 === $person->getAge());

$json = $objectMapper->writeJson($person);
```

Besides a simple class there are multiple ways to specify a source, for example to parse
an array of persons s.

```php
$json = <<<'JSON'
[
{
"firstName": "Ludwig",
"lastName": "Beethoven",
"age": 254
}
]
JSON;

$objectMapper = new ObjectMapper(new SchemaManager());

$personList = $objectMapper->readJson($json, SchemaSource::fromType('array<Person>'));

assert(1 === count($personList));
assert('Ludwig' === $personList[0]->getFirstName());

$json = $objectMapper->writeJson($person);
```

## Code generation

It is possible to transform any DTO class into a [TypeSchema](https://typeschema.org/) specification.
This schema can then be used to generate DTOs in different languages which helps to work with
type-safe objects across different environments.

```php
$schemaManager = new SchemaManager();
$factory = new GeneratorFactory();

$schema = $schemaManager->getSchema(Person::class);

$generator = $factory->getGenerator(GeneratorFactory::TYPE_JAVA, Config::of('org.typeschema.model'));

$result = $generator->generate();

$result->writeTo('/my_model.zip');
```

## TypeSchema specification

It is possible to transform an existing [TypeSchema](https://typeschema.org/) specification into a PHP DTO class.
For example lets take a look at the following specification, which describes a person:

```json
{
"definitions": {
"Person": {
"type": "object",
"type": "struct",
"properties": {
"firstName": {
"type": "string"
Expand All @@ -32,43 +100,45 @@ generate based on this specification the fitting PHP classes.
},
"age": {
"description": "Age in years",
"type": "integer",
"minimum": 0
"type": "integer"
}
},
"required": [
"firstName",
"lastName"
]
}
}
},
"$ref": "Person"
"root": "Person"
}
```

To generate the PHP classes we use the following command:
It is then possible to turn this specification into a ready-to-use PHP class s.

```
vendor/bin/schema schema:parse --format=php schema.json
```php

$schemaManager = new SchemaManager();
$factory = new GeneratorFactory();

$schema = $schemaManager->getSchema(__DIR__ . '/my_schema.json');

$generator = $factory->getGenerator(GeneratorFactory::TYPE_PHP, Config::of('App\\Model'));

$result = $generator->generate();

foreach ($result as $file => $code) {
file_put_contents(__DIR__ . '/' . $file, '<?php' . "\n" . $code);
}
```

Note you can also skip this step and directly write the PHP class by your self. The advantage of starting with a JSON
representation is that you have defined your models in a neutral format so that you can generate the models for
different environments i.e. also for the frontend using the TypeScript generator. The command generates the following
source code:
This would result in the following PHP class:

```php
<?php

declare(strict_types = 1);

#[Required(["firstName", "lastName"])]
class Person implements \JsonSerializable
class Person implements \JsonSerializable, \PSX\Record\RecordableInterface
{
protected ?string $firstName = null;
protected ?string $lastName = null;
#[Description("Age in years")]
#[Minimum(0)]
protected ?int $age = null;
public function setFirstName(?string $firstName) : void
{
Expand All @@ -94,104 +164,48 @@ class Person implements \JsonSerializable
{
return $this->age;
}
public function jsonSerialize()
public function toRecord() : \PSX\Record\RecordInterface
{
return (object) array_filter(array('firstName' => $this->firstName, 'lastName' => $this->lastName, 'age' => $this->age), static function ($value) : bool {
return $value !== null;
});
/** @var \PSX\Record\Record<mixed> $record */
$record = new \PSX\Record\Record();
$record->put('firstName', $this->firstName);
$record->put('lastName', $this->lastName);
$record->put('age', $this->age);
return $record;
}
public function jsonSerialize() : object
{
return (object) $this->toRecord()->getAll();
}
}
```

Now we can parse raw JSON data and fill this in to our object model:

```php
// the data which we want to import
$data = json_decode('{"firstName": "foo", "lastName": "bar"}');

$schemaManager = new SchemaManager();

// we read the schema from the class
$schema = $schemaManager->getSchema(Person::class);

try {
$person = (new SchemaTraverser())->traverse($data, $schema, new TypeVisitor());

// $example contains now an instance of the Person class containing
// the firstName and lastName property
echo $person->getFirstName();

} catch (\PSX\Schema\Exception\ValidationException $e) {
// the validation failed
echo $e->getMessage();
}

```

Every generated PHP class implements also the `JsonSerializable` interface so you can simply encode an object to json.

```php
$schema = new Person();
$schema->setFirstName('foo');
$schema->setLastName('bar');
$schema->setAge(12);
$person = new Person();
$person->setFirstName('foo');
$person->setLastName('bar');
$person->setAge(32);

echo json_encode($schema);
echo json_encode($person);

// would result in
// {"firstName": "foo", "lastName": "bar", "age": 12}
// {"firstName": "foo", "lastName": "bar", "age": 32}

```

## Generator

Beside PHP classes this library can generate the following types:

* CSharp
* Go
* GraphQL
* HTML
* Java
* JsonSchema
* Kotlin
* Markdown
* PHP
* Protobuf
* Python
* Ruby
* Rust
* Swift
* TypeSchema
* TypeScript
* VisualBasic

## Attributes

The following attributes are available:

| Attribute | Target | Example |
|----------------------|----------------|----------------------------------|
| Deprecated | Property | #[Deprecated(true)] |
| Description | Class/Property | #[Description("content")] |
| Discriminator | Property | #[Discriminator("type")] |
| Enum | Property | #[Enum({"foo", "bar"})] |
| Exclude | Property | #[Exclude] |
| ExclusiveMaximum | Property | #[ExclusiveMaximum(true)] |
| ExclusiveMinimum | Property | #[ExclusiveMinimum(true)] |
| Format | Property | #[Format("uri")] |
| Key | Property | #[Key("$ref")] |
| Maximum | Property | #[Maximum(16)] |
| MaxItems | Property | #[MaxItems(16)] |
| MaxLength | Property | #[MaxLength(16)] |
| MaxProperties | Class | #[MaxProperties(16)] |
| Minimum | Property | #[Minimum(4)] |
| MinItems | Property | #[MinItems(4)] |
| MinLength | Property | #[MinLength(4)] |
| MinProperties | Property | #[MinProperties(4)] |
| MultipleOf | Property | #[MultipleOf(2)] |
| Nullable | Property | #[Nullable(true)] |
| Pattern | Property | #[Pattern("A-z+")] |
| Required | Class | #[Required(["name", "title"])] |
| Title | Class | #[Title("foo")] |
| UniqueItems | Property | #[UniqueItems(true)] |

| Attribute | Target | Example |
|---------------|----------------|-----------------------------------------|
| Deprecated | Property | #[Deprecated(true)] |
| DerivedType | Class | #[DerivedType(Person::class, 'person')] |
| Description | Class/Property | #[Description("content")] |
| Discriminator | Class | #[Discriminator('type')] |
| Exclude | Property | #[Exclude] |
| Format | Property | #[Format('date-time')] |
| Key | Property | #[Key('my-complex-name')] |
| Nullable | Property | #[Nullable(true)] |

0 comments on commit 0618f70

Please sign in to comment.