jannisfieml/laravelapigenerator

A small package to generate basic migrations, models, requests, controllers and routes by editing yaml files

dev-main 2021-06-06 15:01 UTC

This package is auto-updated.

Last update: 2024-04-06 20:55:44 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Installation

You can install the package via composer:

composer require jannisfieml/laravelapigenerator

You can publish the config file with:

php artisan vendor:publish --provider="JannisFieml\LaravelApiGenerator\LaravelApiGeneratorServiceProvider" --tag="config"

This is the contents of the published config file:

<?php
// config for Jannisfieml/LaravelApiGenerator

return [
    // Class that every generated Controller extends
    'controller_base_class' => 'App\Http\Controllers\Controller',

    // Class that every generated Model extends
    'model_base_class' => 'Illuminate\Database\Eloquent\Model',
];

Usage

General

This package generates basic functionality for your laravel api. You could use them as they are, but in some cases you need to change some things yourself. The generated files should still give you a great starting point and reduce development time in the beginning of a project.

After you changed your schemas and rerun the commands the generated files will be updated, so keep in mind that your manual changes will be overwritten. Be sure your schemas are as you want them to be and only then change the code or just don't rerun the commands.

Migrations, Models, Requests and Controllers are each split in separate commands, so you can also choose to just update the file-type you didn't modify (i.e. Models).

Start with creating your schemas

This command generates a .yaml file that you can use to write your model definition:

php artisan generate:schema MyModel

The generated 0_my_model.yaml looks like this:

name: "MyModel"
attributes:
    -
        name: "attribute"
        type: "string"
        props: []
        validations: []
hasMany: []
belongsToMany: []

Modify the file as you wish. The name does not need to have the same title as the schema file itself, but it helps keep things organised.

The number prefix is important, as it is used for ordering the migrations. Some table needs to be created before others, so the order the files as the migrations should be executed.

  • name: name of the model
  • attributes: array of model attributes. Numeric ID and timestamps are generated automatically.
    • name: name of attribute
    • type: type that would be used in a migration (i.e. "integer", "string", "foreignId", ...)
    • props: array of props used in migration (i.e. "nullable", "constrained", ...)
    • validations: array of default laravel validations (i.e. "required", "existing:users,id", ...)
  • hasMany: array of models, that this model has HasMany relations to
  • belongsToMany: array of models, that this model has BelongsToMany relations to

This command always generates a new file and does not modify existing ones. Every other command will loop through the schemas directory and will generate files according to your definitions.

For the following examples we will use this schema:

name: "MyModel"
attributes:
    -
        name: "name"
        type: "string"
        props: []
        validations: ["required", "unique:my_model,id"]
    -
        name: "description"
        type: "text"
        props: ["nullable"]
        validations: []
    -
        name: "amount"
        type: "integer"
        props: ["nullable"]
        validations: []
    -
        name: "related_model_id"
        type: "foreignId"
        props: ["constrained"]
        validations: ["required"]
hasMany: []
belongsToMany: []

Generate your migrations

php artisan generate:migrations

The generated migrations will look like this:

<?php

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

class CreateMyModelsTable extends Migration
{
	/**
	 * Run the migrations.
	 *
	 * @return void
	 */
	public function up()
	{
		Schema::create('my_models', function (Blueprint $table) {
			$table->id();
			$table->timestamps();
			$table->string('name');
			$table->text('description')->nullable();
			$table->integer('amount')->nullable();
			$table->foreignId('related_model_id')->constrained();
		});
	}


	/**
	 * Reverse the migrations.
	 *
	 * @return void
	 */
	public function down()
	{
		Schema::dropIfExists('my_models');
	}
}

Run your migrations as usual.

php artisan migrate

Generate your models

php artisan generate:models

As you see, the model will put any defined attributes in the fillable property of the model. Getters and Setters of attributes and BelongsTo methods of foreign-keys are also generated.

The generated models will look like this:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class MyModel extends Model
{
	use HasFactory;

	/**
	 * The table associated with the model.
	 *
	 * @var string
	 */
	protected $table = 'my_models';

	/**
	 * The attributes that are mass assignable.
	 *
	 * @var array
	 */
	protected $fillable = ['name', 'description', 'amount', 'related_model_id'];

	/**
	 * The attributes that should be hidden for arrays.
	 *
	 * @var array
	 */
	protected $hidden = [];

	/**
	 * The attributes that should be cast to native types.
	 *
	 * @var array
	 */
	protected $casts = [];


	public function getId(): int
	{
		return $this->getAttribute('id');
	}


	public function getName(): string
	{
		return $this->getAttribute('name');
	}


	public function setName(string $name): self
	{
		$this->setAttribute('name', $name);

		return $this;
	}


	public function getDescription(): string
	{
		return $this->getAttribute('description');
	}


	public function setDescription(string $description): self
	{
		$this->setAttribute('description', $description);

		return $this;
	}


	public function getAmount(): int
	{
		return $this->getAttribute('amount');
	}


	public function setAmount(int $amount): self
	{
		$this->setAttribute('amount', $amount);

		return $this;
	}


	public function getRelatedModelId(): int
	{
		return $this->getAttribute('related_model_id');
	}


	public function setRelatedModelId(int $related_model_id): self
	{
		$this->setAttribute('related_model_id', $related_model_id);

		return $this;
	}


	public function relatedModel(): BelongsTo
	{
		return $this->belongsTo(RelatedModel::class);
	}
}

Generate requests

php artisan generate:requests

The requests are used in generated Controllers for create and update actions. One specific request is created for those two actions respectively.

The generated requests will look like this:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateMyModelRequest extends FormRequest
{
	public function authorize(): bool
	{
		return true;
	}


	public function rules(): array
	{
		return [
			'name' => 'required|unique:my_model,id|string',
			'description' => 'string',
			'amount' => 'int',
			'related_model_id' => 'required|int',
		];
	}
}

Generate controllers

php artisan generate:controllers

The generated controllers are specifically for api crud actions, so specific ApiResponses are returned and no views. You are free to change this yourself, but as written before the command will overwrite your changes if run again.

The generated controllers will look like this:

<?php

namespace App\Http\Controllers;

use App\Http\Requests\CreateMyModelRequest;
use App\Http\Requests\UpdateMyModelRequest;
use App\Models\MyModel;
use Jannisfieml\LaravelApiGenerator\Responses\ApiDataResponse;
use Jannisfieml\LaravelApiGenerator\Responses\ApiErrorResponse;
use Jannisfieml\LaravelApiGenerator\Responses\ApiResponse;
use Jannisfieml\LaravelApiGenerator\Services\ArrayTransformerService;

class MyModelController extends Controller
{
	public function getMyModels(): ApiResponse
	{
		$data = MyModel::all();

		return new ApiDataResponse($data->toArray());
	}


	public function createMyModel(CreateMyModelRequest $request): ApiResponse
	{
		$arrayTransformerService = new ArrayTransformerService();
		$myModel = new MyModel(
			$arrayTransformerService->transformToSnakeCase($request->toArray())
		);
		$myModel->save();

		return new ApiDataResponse($myModel->toArray());
	}


	public function updateMyModel(UpdateMyModelRequest $request, int $id): ApiResponse
	{
		$arrayTransformerService = new ArrayTransformerService();
		$myModel = MyModel::find($id);
		$myModel->update(
			$arrayTransformerService->transformToSnakeCase($request->toArray())
		);
		$myModel->save();

		return new ApiDataResponse($myModel->toArray());
	}


	public function deleteMyModel(int $id): ApiResponse
	{
		$myModel = MyModel::find($id);
		$myModel->delete();

		return new ApiDataResponse();
	}
}

Generate routes

php artisan generate:routes

The generated routes give your generated controllers routes you can call. You can use your own logic or change the controllers and functions these routes call.

This command will append a comment and the generated routes to the api.php routes file. You can move them above the generated comment and edit them, so a future call of this command won't rewrite them. If you edit the comment itself an execution of the command will append everything to the api.php file again as if the command was never run before (The command is used to identify the auto-generated section of the api.php route file).

The generated routes will look like this:

<?php

use Illuminate\Support\Facades\Route;

// ... probably your other routes if you have any

/*
|--------------------------------------------------------------------------
| These are auto-generated routes of @JannisFieml/apigenerator
| Please don't edit this comment or any lines below!
| Otherwise automated updates of this code won't be possible.
| ...but I am just a comment, so do what you want
|--------------------------------------------------------------------------
*/

Route::middleware([])->group(function () {
	Route::get('/related-models', [App\Http\Controllers\RelatedModelController::class, 'getRelatedModels']);
	Route::post('/related-models', [App\Http\Controllers\RelatedModelController::class, 'createRelatedModel']);
	Route::put('/related-models/{id}', [App\Http\Controllers\RelatedModelController::class, 'updateRelatedModel']);
	Route::delete('/related-models/{id}', [App\Http\Controllers\RelatedModelController::class, 'deleteRelatedModel']);
});

Route::middleware([])->group(function () {
	Route::get('/main-models', [App\Http\Controllers\MainModelController::class, 'getMainModels']);
	Route::post('/main-models', [App\Http\Controllers\MainModelController::class, 'createMainModel']);
	Route::put('/main-models/{id}', [App\Http\Controllers\MainModelController::class, 'updateMainModel']);
	Route::delete('/main-models/{id}', [App\Http\Controllers\MainModelController::class, 'deleteMainModel']);
});

Generate insomnia export

php artisan generate:insomnia

This command generates a file that you can import in insomnia to test your api-routes. This command only generates an export for the routes / controllers that this package will generate.

You can find your exports in the insomnia directory in your project root.

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.