PHP code example of langsys / openapi-docs-generator

1. Go to this page and download the library: Download langsys/openapi-docs-generator library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

langsys / openapi-docs-generator example snippets


namespace App\DataObjects;

use Spatie\LaravelData\Data;

class UserData extends Data
{
    public function __construct(
        public int $id,
        public string $name,
        public string $email,
        public ?string $phone,
        public bool $is_active = true,
    ) {}
}

use Langsys\OpenApiDocsGenerator\Generators\Attributes\Example;

class UserData extends Data
{
    public function __construct(
        #[Example(42)]
        public int $id,

        #[Example('[email protected]')]
        public string $email,

        #[Example(true)]
        public bool $is_admin,
    ) {}
}

#[Example(':sentence')]
public string $title,

#[Example(':numberBetween', arguments: [1, 100])]
public int $score,

use Langsys\OpenApiDocsGenerator\Generators\Attributes\Description;

class UserData extends Data
{
    public function __construct(
        #[Description('The unique user identifier')]
        public int $id,

        #[Description('ISO 8601 date when the account was created')]
        public string $created_at,
    ) {}
}

use Langsys\OpenApiDocsGenerator\Generators\Attributes\Omit;

class UserData extends Data
{
    public function __construct(
        public int $id,
        public string $name,

        #[Omit]
        public string $internal_token,  // will NOT appear in the schema
    ) {}
}

use Langsys\OpenApiDocsGenerator\Generators\Attributes\GroupedCollection;

class TranslationData extends Data
{
    public function __construct(
        #[GroupedCollection('en')]
        #[Example('Hello')]
        public array $greetings,
    ) {}
}

use Langsys\OpenApiDocsGenerator\Generators\Attributes\ItemType;
use Langsys\OpenApiDocsGenerator\Generators\Attributes\OneOfItemsFrom;

#[ItemType('blocks')] // handle inferred as "paragraph"
class ParagraphResource extends Data { /* ... */ }

#[ItemType('blocks', 'picture')] // handle overridden to "picture"
class ImageResource extends Data { /* ... */ }

class BlockContainerResource extends Data
{
    public function __construct(
        public string $title,
        #[OneOfItemsFrom('blocks')]
        public array $content,
    ) {}
}

enum UserStatus: string
{
    case Active = 'active';
    case Inactive = 'inactive';
    case Suspended = 'suspended';
}

class UserData extends Data
{
    public function __construct(
        #[Example('active')]
        public UserStatus $status = UserStatus::Active,
    ) {}
}

class AddressData extends Data
{
    public function __construct(
        public string $street,
        public string $city,
    ) {}
}

class UserData extends Data
{
    public function __construct(
        public string $name,
        public AddressData $address,
    ) {}
}

use Carbon\Carbon;

class EventData extends Data
{
    public function __construct(
        public string $title,
        public Carbon $starts_at,
        public ?Carbon $cancelled_at = null,

        #[Example('2025-12-31T23:59:59+00:00')]
        public Carbon $deadline,
    ) {}
}

use Spatie\LaravelData\Data;
use Spatie\LaravelData\Optional;

class UpdateUserRequest extends Data
{
    public function __construct(
        public string $id,                        // t|Optional $age,                 // optional — type resolves to integer
        public UserStatus|Optional $status = UserStatus::Active,  // optional enum with default
    ) {}
}

class OrderData extends Data
{
    public function __construct(
        public string $order_number,

        /** @var OrderItemData[] */
        public array $items,
    ) {}
}

use Illuminate\Support\Collection;

class OrderData extends Data
{
    public function __construct(
        /** @var Collection<int, OrderItemData> */
        public Collection $items,
    ) {}
}

class CatalogData extends Data
{
    public function __construct(
        /** @var ProductData[] */
        #[GroupedCollection('electronics')]
        public array $products_by_category,
    ) {}
}

use Spatie\LaravelData\DataCollection;
use Spatie\LaravelData\Attributes\DataCollectionOf;

class OrderData extends Data
{
    public function __construct(
        #[DataCollectionOf(OrderItemData::class)]
        public DataCollection $items,
    ) {}
}

class CatalogData extends Data
{
    public function __construct(
        #[GroupedCollection('electronics')]
        #[DataCollectionOf(ProductData::class)]
        public DataCollection $products_by_category,
    ) {}
}

'pagination_fields' => [
    ['name' => 'status', 'description' => 'Response status', 'content' => true, 'type' => 'bool'],
    ['name' => 'page', 'description' => 'Current page number', 'content' => 1, 'type' => 'int'],
    ['name' => 'records_per_page', 'description' => 'Records per page', 'content' => 8, 'type' => 'int'],
    ['name' => 'page_count', 'description' => 'Number of pages', 'content' => 5, 'type' => 'int'],
    ['name' => 'total_records', 'description' => 'Total items', 'content' => 40, 'type' => 'int'],
],

// config/openapi-docs.php
'faker_attribute_mapper' => [
    'address_1' => 'streetAddress',   // $user->address_1 -> Faker::streetAddress()
    'address_2' => 'buildingNumber',  // $user->address_2 -> Faker::buildingNumber()
    'zip'       => 'postcode',        // $user->zip_code  -> Faker::postcode()
    '_at'       => 'date',            // $user->created_at -> Faker::date()
    '_url'      => 'url',             // $user->avatar_url -> Faker::url()
    'locale'    => 'locale',          // $user->locale     -> Faker::locale()
    'phone'     => 'phoneNumber',     // $user->phone      -> Faker::phoneNumber()
    '_id'       => 'id',              // $user->user_id    -> custom 'id' function
],

// config/openapi-docs.php
'custom_functions' => [
    'id' => [\Langsys\OpenApiDocsGenerator\Functions\CustomFunctions::class, 'id'],
    'date' => [\Langsys\OpenApiDocsGenerator\Functions\CustomFunctions::class, 'date'],
],

namespace App\OpenApi;

class MyCustomFunctions
{
    public function currency(string $type): string
    {
        return collect(['USD', 'EUR', 'GBP'])->random();
    }

    public function percentage(string $type): int|string
    {
        return $type === 'int' ? random_int(0, 100) : random_int(0, 100) . '%';
    }
}

'custom_functions' => [
    'currency' => [App\OpenApi\MyCustomFunctions::class, 'currency'],
    'percentage' => [App\OpenApi\MyCustomFunctions::class, 'percentage'],
],

#[Example(':sentence')]
public string $title,

#[Example(':numberBetween', arguments: [1, 1000])]
public int $score,

#[Example(':email')]
public string $contact_email,

namespace App\DataObjects;

use Spatie\LaravelData\Data;

final class UserData extends Data
{
    public function __construct(
        public string $id,
        public string $name,
        public string $email,
        public ?string $email_verified_at,
        public string $password,
        public ?string $remember_token,
        public ?string $created_at,
        public ?string $updated_at,
    ) {}
}

'documentations' => [
    'v1' => [
        'paths' => [
            'docs_json' => 'v1-api-docs.json',
            'annotations' => [app_path('Http/Controllers/V1'), app_path('DataObjects/V1')],
        ],
    ],
    'v2' => [
        'paths' => [
            'docs_json' => 'v2-api-docs.json',
            'annotations' => [app_path('Http/Controllers/V2'), app_path('DataObjects/V2')],
        ],
    ],
],

'paths' => [
    // Directory where api-docs.json and api-docs.yaml are written
    'docs' => storage_path('api-docs'),

    // Base server URL added to the OpenAPI servers list (null = no server entry)
    'base' => env('OPENAPI_BASE_PATH', null),

    // Directories to exclude from scanning
    'excludes' => [],
],

'security_definitions' => [
    // Define authentication schemes your API supports.
    // These appear in components/securitySchemes in the OpenAPI output.
    'security_schemes' => [
        // Bearer token (e.g. Laravel Sanctum)
        'bearerAuth' => [
            'type' => 'http',
            'scheme' => 'bearer',
            'description' => 'Enter token in format: Bearer <token>',
        ],

        // API key sent as a custom header
        'apiKey' => [
            'type' => 'apiKey',
            'name' => 'X-Authorization',   // header name
            'in' => 'header',              // where the key is sent
            'description' => 'API key for machine-to-machine access',
        ],

        // OAuth2 (e.g. Laravel Passport)
        // 'passport' => [
        //     'type' => 'oauth2',
        //     'flows' => [
        //         'password' => [
        //             'authorizationUrl' => '/oauth/authorize',
        //             'tokenUrl' => '/oauth/token',
        //             'refreshUrl' => '/oauth/token/refresh',
        //             'scopes' => [],
        //         ],
        //     ],
        // ],
    ],

    // Global security 

'generate_yaml_copy' => true,

'paths' => [
    'base' => env('OPENAPI_BASE_PATH', 'https://api.example.com/v1'),
],

'constants' => [
    'API_HOST' => env('API_HOST', 'http://localhost'),
    'API_VERSION' => 'v1',
],

#[OA\Server(url: API_HOST)]
#[OA\Info(title: 'My API', version: API_VERSION)]

'scan_options' => [
    'processors' => [],
    'exclude' => [],
    'open_api_spec_version' => env('OPENAPI_SPEC_VERSION', '3.0.0'),
],

'exclude' => [
    'app/Http/Controllers/Internal',
    'app/Http/Controllers/Admin',
],

'open_api_spec_version' => '3.1.0',

namespace App\Swagger;

use OpenApi\Analysis;
use OpenApi\Annotations\OpenApi;
use OpenApi\Annotations\Tag;

class TagOrderProcessor
{
    public function __invoke(Analysis $analysis): void
    {
        if (!isset($analysis->openapi)) {
            return;
        }

        /** @var OpenApi $openapi */
        $openapi = $analysis->openapi;

        // Define your tags in the order you want them to appear in Swagger UI.
        // Each name must match a tag used in your controller annotations.
        // Example: #[OA\Get(tags: ['Users'])]
        $openapi->tags = [
            new Tag(['name' => 'Auth']),
            new Tag(['name' => 'Users']),
            new Tag(['name' => 'Projects']),
            new Tag(['name' => 'Billing']),
            // Add all your tags here in the desired order...
        ];
    }
}

'scan_options' => [
    'processors' => [
        new \App\Swagger\TagOrderProcessor(),
    ],
],

'thunder_client' => [
    'auth' => [
        // Bearer token auth (e.g. Sanctum, Passport)
        // Sets auth type to "bearer" in Thunder Client.
        // The actual token value is stored in your Thunder Client environment
        // as the variable named in 'token_variable'.
        'bearerAuth' => [
            'type' => 'bearer',
            'token_variable' => 'token',  // TC environment variable name
        ],

        // API key sent as a custom header
        // Adds the header to the request and sets auth type to "none"
        // (since auth is handled via the header itself).
        'apiKey' => [
            'type' => 'header',
            'header_name' => 'X-Authorization',  // header added to request
            'value' => '{{api_key}}',             // TC variable reference
        ],

        // Basic auth
        // Sets auth type to "basic" in Thunder Client.
        // Username/password are configured in Thunder Client's auth tab.
        // 'basicAuth' => [
        //     'type' => 'basic',
        // ],
    ],

    // When an endpoint has no security annotation, this scheme is used.
    // Set to 'none' to leave requests unauthenticated by default.
    'default_auth' => 'bearerAuth',
],

'thunder_client' => [
    'environment' => [
        'slug' => 'local',       // filename: tc_env_local.json
        'name' => 'Local',       // display name in Thunder Client

        // Map Thunder Client variable names to values.
        // 'env:KEY' reads the value from your Laravel .env file.
        // Any other value is used as-is (empty string = user fills in manually).
        'variables' => [
            'url' => 'env:APP_URL',       // reads APP_URL from .env
            'token' => '',                 // empty -- user pastes their token
            'api_key' => 'env:API_KEY',   // reads API_KEY from .env
        ],

        // Appended to the base URL variable when its value comes from env:
        // e.g. APP_URL=http://localhost -> url=http://localhost/api
        'url_suffix' => '/api',
    ],
],

// In your controller:
#[OA\Get(path: '/api/users', tags: ['Users'])]
//                                    ^^^^^^^
// -> Goes into "Users" folder in Thunder Client

'skip_path_segments' => ['api', 'v1', 'v2', 'v3'],

'thunder_client' => [
    // Thunder Client workspace root directory.
    // Collections are written to {output_dir}/collections/
    // Environment files are written to {output_dir}/
    'output_dir' => base_path('thunder-tests'),

    // Slug used in the collection filename: tc_col_{slug}.json
    // Use a descriptive name if you have multiple collections.
    'collection_slug' => 'api',

    // Display name shown in Thunder Client's collection list.
    // When null, uses the 'info.title' from your OpenAPI spec.
    'collection_name' => null,

    // Thunder Client variable name used as the base URL prefix.
    // All request URLs are generated as: {{url}}/path/here
    // The actual value of this variable is set in your TC environment.
    'base_url_variable' => 'url',

    // Auth scheme mappings -- see "Auth Configuration" section above.
    'auth' => [
        'sanctum' => [
            'type' => 'bearer',
            'token_variable' => 'token',
        ],
    ],

    // Default auth scheme when an endpoint has no security annotation.
    // Must match a key in the 'auth' array above, or 'none'.
    'default_auth' => 'sanctum',

    // Environment file generation -- see "Environment File" section above.
    // Set to null to skip.
    'environment' => [
        'slug' => 'local',
        'name' => 'Local',
        'variables' => [
            'url' => 'env:APP_URL',
        ],
        'url_suffix' => '/api',
    ],

    // URL path segments to ignore when inferring folder names from paths.
    // Only used as a fallback when endpoints have no OpenAPI tags.
    'skip_path_segments' => ['api', 'v1', 'v2', 'v3'],

    // Headers added to every generated request.
    // Uses 'name'/'value' format (not 'key'/'value').
    'default_headers' => [
        ['name' => 'Accept', 'value' => 'application/json'],
    ],
],

use Langsys\OpenApiDocsGenerator\OpenApiDocsFacade as OpenApiDocs;

OpenApiDocs::generateDocs();

use Langsys\OpenApiDocsGenerator\Generators\OpenApiGenerator;

app(OpenApiGenerator::class)->generateDocs();

use Langsys\OpenApiDocsGenerator\Generators\GeneratorFactory;

GeneratorFactory::make('v2')->generateDocs();

use Langsys\OpenApiDocsGenerator\Generators\ThunderClientFactory;

ThunderClientFactory::make('default')->generate();
bash
php artisan vendor:publish --provider="Langsys\OpenApiDocsGenerator\OpenApiDocsServiceProvider" --tag=config
bash
php artisan openapi:generate
json
{
  "items": {
    "type": "array",
    "items": { "$ref": "#/components/schemas/OrderItemData" }
  }
}
bash
php artisan openapi:dto --model=App\\Models\\User
bash
php artisan openapi:make-processor
bash
php artisan openapi:generate