PHP code example of rjapi / raml-json-api

1. Go to this page and download the library: Download rjapi/raml-json-api 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/ */

    

rjapi / raml-json-api example snippets




namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\JsonResponse;
use SoliDry\Exceptions\ErrorHandler;

class Handler extends ExceptionHandler
{
    use ErrorHandler; 
    
    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request $request
     * @param  \Exception $exception
     * @return JsonResponse
     */
    public function render($request, Exception $exception): JsonResponse
    {
        return $this->renderJsonApi($request, $exception); // method you should call to return json-api response
    }
}

{
  "errors": [
    {
      "code": 61,
      "message": "Connection refused [tcp://127.0.0.1:6379]",
      "file": "/vendor/predis/predis/src/Connection/AbstractConnection.php",
      "line": 155,
      "uri": "http://laravel.loc/api/v2/article",
      "meta": "#0 /vendor/predis/predis/src/Connection/StreamConnection.php(128): Predis\\Connection\\AbstractConnection->onConnectionError('Connection refu...', 61)\n#1 /vendor/predis/predis/src/Connection/StreamConnection.php(178): Predis\\Connection\\StreamConnection->createStreamSocket(Object(Predis\\Connection\\Parameters), 'tcp://127.0.0.1...', 4)\n#2 /vendor/predis/predis/src/Connection/StreamConnection.php(100): Predis\\Connection\\StreamConnection->tcpStreamInitializer(Object(Predis\\Connection\\Parameters))\n#3 /vendor/predis/predis/src/Connection/AbstractConnection.php(81): Predis\\Connection\\StreamConnection->createResource()\n#4 /vendor/predis/predis/src/Connection/StreamConnection.php(258): Predis\\Connection\\AbstractConnection->connect()\n#5 /vendor/predis/predis/src/Connection/AbstractConnection.php(180): Predis\\Connection\\StreamConnection->connect()\n#6 /vendor/predis/predis/src/Connection/StreamConnection.php(288): Predis\\Connection\\AbstractConnection->getResource()\n#7 /vendor/predis/predis/src/Connection/StreamConnection.php(394): Predis\\Connection\\StreamConnection->write('*2\\r\\n$3\\r\\nGET\\r\\n$4...')\n#8 /vendor/predis/predis/src/Connection/AbstractConnection.php(110): Predis\\Connection\\StreamConnection->writeRequest(Object(Predis\\Command\\StringGet))\n#9 /vendor/predis/predis/src/Client.php(331): Predis\\Connection\\AbstractConnection->executeCommand(Object(Predis\\Command\\StringGet))\n#10 /vendor/predis/predis/src/Client.php(314): Predis\\Client->executeCommand(Object(Predis\\Command\\StringGet))\n#11 /vendor/laravel/framework/src/Illuminate/Redis/Connections/Connection.php(114): Predis\\Client->__call('get', Array)\n#12 /vendor/laravel/framework/src/Illuminate/Redis/Connections/Connection.php(214): Illuminate\\Redis\\Connections\\Connection->command('get', Array)\n#13 /vendor/laravel/framework/src/Illuminate/Redis/RedisManager.php(195): Illuminate\\Redis\\Connections\\Connection->__call('get', Array)\n#14 /vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php(237): Illuminate\\Redis\\RedisManager->__call('get', Array)\n#15 /vendor/solidry/api-generator/src/Extension/CacheTrait.php(95): Illuminate\\Support\\Facades\\Facade::__callStatic('get', Array)\n#16 /vendor/solidry/api-generator/src/Extension/CacheTrait.php(60): SoliDry\\Extension\\ApiController->getXFetched(Object(Illuminate\\Http\\Request), Object(SoliDry\\Helpers\\SqlOptions))\n#17 /vendor/solidry/api-generator/src/Extension/ApiController.php(115): SoliDry\\Extension\\ApiController->getCached(Object(Illuminate\\Http\\Request), Object(SoliDry\\Helpers\\SqlOptions))\n#18 [internal function]: SoliDry\\Extension\\ApiController->index(Object(Illuminate\\Http\\Request))\n#19 /vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array(Array, Array)\n#20 /vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction('index', Array)\n#21 /vendor/laravel/framework/src/Illuminate/Routing/Route.php(219): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(Modules\\V2\\Http\\Controllers\\ArticleController), 'index')\n#22 /vendor/laravel/framework/src/Illuminate/Routing/Route.php(176): Illuminate\\Routing\\Route->runController()\n#23 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(680): Illuminate\\Routing\\Route->run()\n#24 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))\n#25 /vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(41): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))\n#26 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))\n#27 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))\n#28 /vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(58): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))\n#29 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): Illuminate\\Routing\\Middleware\\ThrottleRequests->handle(Object(Illuminate\\Http\\Request), Object(Closure), 60, '1')\n#30 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))\n#31 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))\n#32 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(682): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))\n#33 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(657): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))\n#34 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(623): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))\n#35 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(612): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))\n#36 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))\n#37 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))\n#38 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))\n#39 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))\n#40 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))\n#41 /public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))\n#42 {main}"
    }
  ]
}



namespace App\Providers;

use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;

class RouteServiceProvider extends ServiceProvider
{
    // ...

    protected function mapApiRoutes()
    {
        // Route::prefix('api') // you don't need prefixes then
        Route::middleware('api')
             ->namespace($this->namespace)
             ->group(base_path('routes/api.php'));
    }
}

Modules/{ModuleName}/Http/Controllers/ - contains Controllers that extends the DefaultController (descendant of Laravel's Controller)
Modules/{ModuleName}/Http/FormRequest/ - contains forms that extends the BaseFormRequest (descendant of Laravel's FormRequest) and validates input attributes (that were previously defined as *Attributes)
Modules/{ModuleName}/Entities/ - contains mappers that extends the BaseModel (descendant of Laravel's Model) and maps attributes to RDBMS
Modules/{ModuleName}/Routes/api.php - contains routings pointing to Controllers with JSON API protocol support
Modules/{ModuleName}/Database/Migrations/ - contains migrations created with option --migrations


namespace Modules\V3\Http\Controllers;

class ArticleController extends DefaultController 
{
    // >>>props>>>
    // <<<props<<<
    // >>>methods>>>
    /**
    * @OA\Get(
    *     path="/v3/article",
    *     summary="Get Articles ",
    *     tags={"ArticleController"},
    *     @OA\Parameter(
    *         in="query",
    *         name="include",
    *           equired=false,
    *         @OA\Schema(
    *             type="string",
    *         ),
    *     ),
    *     @OA\Parameter(
    *         in="query",
    *         name="filter",
    *         ry",
    *         name="include",
    *          * )
    */

    /**
    * @OA\Patch(
    *     path="/v3/article/{id}",
    *     summary="Update Article",
    *     tags={"ArticleController"},
    *     @OA\Response(
    *         response="200",
    *         description="",
    *     ),
    * )
    */

    /**
    * @OA\Delete(
    *     path="/v3/article/{id}",
    *     summary="Delete Article",
    *     tags={"ArticleController"},
    *     @OA\Response(
    *         response="204",
    *         description="",
    *     ),
    * )
    */

    /**
    * @OA\Get(
    *     path="/v3/article/{id}/{related}",
    *     summary="Get Article related objects",
    *     tags={"ArticleController"},
    *     @OA\Parameter(
    *         in="query",
    *         name="data",
    *           *         response="200",
    *         description="",
    *     ),
    * )
    */

    /**
    * @OA\Post(
    *     path="/v3/article/{id}/relationships/{relations}",
    *     summary="Create Article relation object",
    *     tags={"ArticleController"},
    *     @OA\Parameter(
    *         in="path",
    *         name="id",
    *         ="",
    *     ),
    * )
    */

    /**
    * @OA\Post(
    *     path="/v3/article/bulk",
    *     summary="Create Article bulk",
    *     tags={"ArticleController"},
    *     @OA\Response(
    *         response="201",
    *         description="",
    *     ),
    * )
    */

    /**
    * @OA\Patch(
    *     path="/v3/article/bulk",
    *     summary="Update Article bulk",
    *     tags={"ArticleController"},
    *     @OA\Response(
    *         response="200",
    *         description="",
    *     ),
    * )
    */

    /**
    * @OA\Delete(
    *     path="/v3/article/bulk",
    *     summary="Delete Article bulk",
    *     tags={"ArticleController"},
    *     @OA\Response(
    *         response="204",
    *         description="",
    *     ),
    * )
    */

    // <<<methods<<<
}


return [
    'name' => 'V1',
    'query_params'=> [
        'limit' => 15,
        'sort' => 'desc',
        'access_token' => 'db7329d5a3f381875ea6ce7e28fe1ea536d0acaf',
    ],
    'trees'=> [
        'menu' => true,
    ],
    'jwt'=> [
        'enabled' => true,
        'table' => 'user',
        'activate' => 30,
        'expires' => 3600,
    ],
    'state_machine'=> [
        'article'=> [
            'status'=> [
                'enabled' => true,
                'states'=> [
                    'initial' => ['draft'],
                    'draft' => ['published'],
                    'published' => ['archived', 'postponed'],
                    'postponed' => ['published', 'archived'],
                    'archived' => [''],
                ],
            ],
        ],
    ],
    'spell_check'=> [
        'article'=> [
            'description'=> [
                'enabled' => true,
                'language' => 'en',
            ],
        ],
    ],
    'bit_mask'=> [
        'user'=> [
            'permissions'=> [
                'enabled' => true,
                'hide_mask' => true,
                'flags'=> [
                'publisher' => 1,
                'editor' => 2,
                'manager' => 4,
                'photo_reporter' => 8,
                'admin' => 16,
                ],
            ],
        ],
    ],
    'cache'=> [
        'tag'=> [
            'enabled' => true,
            'stampede_xfetch' => false,
            'stampede_beta' => 1.1,
            'ttl' => 3600,
        ],
        'article'=> [
            'enabled' => true,
            'stampede_xfetch' => true,
            'stampede_beta' => 1.5,
            'ttl' => 300,
        ],
    ],    
];


namespace Modules\V1\Http\Controllers;

class ArticleController extends DefaultController 
{
}


namespace Modules\V1\Http\Controllers;

use SoliDry\Extension\BaseController;

class DefaultController extends BaseController 
{
}


namespace Modules\V2\Http\Requests;

use SoliDry\Extension\BaseFormRequest;

class ArticleFormRequest extends BaseFormRequest 
{
    // >>>props>>>
    public $id = null;
    // Attributes
    public $title = null;
    public $description = null;
    public $url = null;
    public $show_in_top = null;
    public $status = null;
    public $topic_id = null;
    public $rate = null;
    public $date_posted = null;
    public $time_to_live = null;
    public $deleted_at = null;
    // <<<props<<<

    // >>>methods>>>
    public function authorize(): bool 
    {
        return true;
    }

    public function rules(): array 
    {
        return [
            'title' => ' ];
    }
    // <<<methods<<<
}


namespace Modules\V2\Entities;

use Illuminate\Database\Eloquent\SoftDeletes;
use SoliDry\Extension\BaseModel;

class Article extends BaseModel 
{
    use SoftDeletes;

    // >>>props>>>
    protected $dates = ['deleted_at'];
    protected $primaryKey = 'id';
    protected $table = 'article';
    public $timestamps = false;
    public $incrementing = false;
    // <<<props<<<
    // >>>methods>>>

    public function tag() 
    {
        return $this->belongsToMany(Tag::class, 'tag_article');
    }
    public function topic() 
    {
        return $this->belongsTo(Topic::class);
    }
    // <<<methods<<<
}

// >>>routes>>>
// Article routes
Route::group(['prefix' => 'v2', 'namespace' => 'Modules\\V2\\Http\\Controllers'], function()
{
    // bulk routes
    Route::post('/article/bulk', 'ArticleController@createBulk');
    Route::patch('/article/bulk', 'ArticleController@updateBulk');
    Route::delete('/article/bulk', 'ArticleController@deleteBulk');
    // basic routes
    Route::get('/article', 'ArticleController@index');
    Route::get('/article/{id}', 'ArticleController@view');
    Route::post('/article', 'ArticleController@create');
    Route::patch('/article/{id}', 'ArticleController@update');
    Route::delete('/article/{id}', 'ArticleController@delete');
    // relation routes
    Route::get('/article/relationships/{relations}', 'ArticleController@relations');
    Route::post('/article/relationships/{relations}', 'ArticleController@createRelations');
    Route::patch('/article/relationships/{relations}', 'ArticleController@updateRelations');
    Route::delete('/article/relationships/{relations}', 'ArticleController@deleteRelations');
});
// <<<routes<<<


use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateArticleTable extends Migration 
{
    public function up() 
    {
        Schema::create('article', function(Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title', 256);
            $table->index('title', 'idx_title');
            $table->string('description', 1024);
            $table->string('url', 255);
            $table->unique('url', 'idx_url');
            // Show at the top of main page
            $table->unsignedTinyInteger('show_in_top');
            $table->enum('status', ["draft","published","postponed","archived"]);
            // ManyToOne Topic relationship
            $table->unsignedInteger('topic_id');
            $table->foreign('topic_id', 'idx_fk_topic_id')->references('id')->on('topic')->onDelete('cascade')->onUpdate('cascade');
            $table->timestamps();
        });
    }

    public function down() 
    {
        Schema::dropIfExists('article');
    }

}

http://example.com/api/v1/article?

http://example.com/api/v1/article/1?

http://example.com/api/v1/article?

http://example.com/api/v1/article?


return [
    'modules'=> [
        'v1',
    ]
];


return [
    'name'=>'V1',
    'query_params'=> [
        // default settings
        'limit' => 15,
        'sort' => 'desc',
        // access token to check via global FormRequest
        'access_token' => 'db7329d5a3f381875ea6ce7e28fe1ea536d0acaf',
    ],
];

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \Modules\V2\Http\Requests\ApiAccessToken::class,
    ];

    'query_params'=> [
        'limit' => 15,
        'sort' => 'desc',
        'access_token' => 'db7329d5a3f381875ea6ce7e28fe1ea536d0acaf',
    ],

    'jwt'=> [
        'enabled' => true,
        'table' => 'user',
        'activate' => 30,
        'expires' => 3600,
    ],

'jwt_secret'     => env('JWT_SECRET', 'secret'),

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'jwt' => \SoliDry\Extension\BaseJwt::class,    

Route::get('/article', 'ArticleController@index')->middleware('jwt');

Route::group(['middleware' => 'jwt', 

'cache'=> [
    'article'=> [
        'enabled' => true,
        'stampede_xfetch' => true,
        'stampede_beta' => 1.5,
        'ttl' => 300,
    ],
],


namespace Modules\V2\Entities;

use Illuminate\Database\Eloquent\SoftDeletes;
use SoliDry\Extension\BaseModel;

class Article extends BaseModel 
{
    use SoftDeletes;

    // >>>props>>>
    protected $dates = ['deleted_at'];
    // ...
}


use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateArticleTable extends Migration 
{
    public function up() 
    {
        Schema::create('article', function(Blueprint $table) {
            // ...
            $table->softDeletes();
            // ...
        });
    }
    // ...
}


namespace Modules\V1\Http\Controllers;

use SoliDry\Extension\BaseController;

class DefaultController extends BaseController 
{
    protected $jsonApi = false;
}

    'state_machine'=> [
        'article'=> [
            'status'=> [
                'enabled'=>true,
                'states'=> [
                    'initial' => ['draft'],
                    'draft' => ['published'],
                    'published' => ['archived', 'postponed'],
                    'postponed' => ['published', 'archived'],
                    'archived' => [''],
                ],
            ],
        ],
    ],

    'spell_check'=> [
        'article'=> [
            'description'=> [
                'enabled'=>true,
                'language' => 'en',
            ],
        ],
    ],

'bit_mask'=> [
    'user'=> [
        'permissions'=> [
            'enabled' => true,
            'flags'=> [
                'publisher' => 1,
                'editor' => 2,
                'manager' => 4,
                'photo_reporter' => 8,
                'admin' => 16,
            ],
        ],
    ],
],

    'custom_sql'    => [
        'article' => [
            'enabled' => true,
            'query'   => 'SELECT id, title FROM article a INNER JOIN tag_article ta ON ta.article_id=a.id 
                          WHERE ta.tag_id IN (
                          SELECT id FROM tag WHERE CHAR_LENGTH(title) > :tag_len
                          ) ORDER BY a.id DESC',
            'bindings' => [
                'tag_len' => 5,
            ]
        ],
    ],  

    protected $fillable = [
        'id',
        'title'
    ];


namespace Modules\V1\Http\Controllers;

use Illuminate\Http\Request;

class ArticleController extends DefaultController
{
    public function create(Request $request)
    {
        // any business logic here for input pre-processing data
        parent::create($request);
        // any business logic here for output pre-processing data
    }
}


namespace Modules\V1\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Route;
use SoliDry\Extension\BaseController;

class DefaultController extends BaseController 
{
    public function __construct(Route $route)
    {
        // specific code before init 
        parent::__construct($route);
        // specific code after init 
    }
    
    public function index(Request $request)
    {
        // specific code before index execution
        parent::index($request); 
        // specific code after index execution
    }
}


namespace Modules\V2\Http\Controllers;

class ArticleController extends DefaultController 
{
    private $prop = 'foo';

    // >>>props>>>
    // <<<props<<<
    
    public function myMethod()
    {
        return true;
    }
}


namespace Modules\V2\Http\Requests;

use SoliDry\Extension\BaseFormRequest;

class TagFormRequest extends BaseFormRequest
{
    public $userPropOne = true;
    // >>>props>>>
    public $id = null;
    // Attributes
    public $title = null;
    // <<<props<<<
    public $userPropTwo = 123;


    public function userDefinedMethod(): int
    {
        return 1;
    }

    // >>>methods>>>
    public function authorize(): bool 
    {
        return true;
    }

    public function rules(): array 
    {
        return [
            "title" => "string|


namespace Modules\V1\Entities;

use SoliDry\Extension\BaseModel;

class Article extends BaseModel 
{
    public $userPropOne = true;
    // >>>props>>>
    protected $primaryKey = "id";
    protected $table = "article";
    public $timestamps = false;
    // <<<props<<<
    public $userPropTwo = 123;

    public function userDefinedMethod(): int
    {
        return 1;
    }

    // >>>methods>>>

    public function tag() 
    {
        return $this->belongsToMany(Tag::class, 'tag_article');
    }
    public function topic() 
    {
        return $this->belongsTo(Topic::class);
    }
    // <<<methods<<<

    public function anotherUserDefinedMethod(): bool
    {
        return false;
    }
} 


use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddColumnLastNameToUser extends Migration 
{
    public function up() 
    {
        Schema::table('user', function(Blueprint $table) {
            $table->string('last_name', 256);
            $table->index(['first_name', 'last_name']);
            $table->unsignedBigInteger('permissions');
        });
    }

    public function down() 
    {
        Schema::table('user', function(Blueprint $table) {
            $table->dropColumn('last_name');
            $table->dropColumn('permissions');
        });
    }

}

composer dump-autoload

php artisan api:generate oas/openapi.yaml --migrations
/Modules/{ModuleName}/Routes/api.php
 php artisan module:migrate 
bash
php artisan api:generate oas/openapi.yaml --migrations --tests
bash
tests/functional/ArticleCest.php created
...
tests/functional/TagCest.php created
config/module.php
config/module.php
Modules/{ModuleName}/Config/config.php
Modules/{ModuleName}/Config/config.php
Modules\{ModuleName}\Http\Requests\ApiAccessToken.php
app/Http/Kernel.php
Modules/{ModuleName}/Config/config.php
app/Http/Kernel.php
Modules/{ModuleName}/Routes/api.php
zsh
apt-get install php-pspell
sh  
  php artisan api:generate oas/openapi.yaml --migrations --regenerate --merge=last