dotcubed.io/laravel-rad-mvc

RAD (rapid application development) MVC

0.2 2024-04-20 17:47 UTC

This package is auto-updated.

Last update: 2024-04-20 17:49:27 UTC


README

This repo enables a regular Laravel install to use a RAD (Rapid Application Development) approach for API-based projects.

By using naming conventions to automatically trigger Model methods, encapsulate them with error handling, validate with Form Requests and transform them with Resources less code is required and quicker development times can be achieved.

Regular usage of MVC for an API-based project goes as Route > Controller > Model > Response.

What this library allows is the same workflow but cleaner since a Controller method is replaced by a magic call that can parse and identify the Model method it needs to trigger and return its value.

If a scenario requires an actual Controller method to be used than just by creating it on the Controller will allow the regular workflow to be used.

How-to

Request workflow

A Route points to a Controller/Method. This Controller and Method name will define what Form Request, Resource and Model will be fetched, instanced and triggered.

During the Request/Response you may use a Form Request to validate data and a Resource to format the data that will be returned.

Initial Configuration

For a Controller to be able to manipulate the Request, it needs to extend this librarie's own controller as such:

class MyNewController extends \Dotcubed\LaravelRadMvc\Controllers\ApiController

When a Route hits the controller it will trigger the magic method parsing.

Naming convention

For this structure to work a couple of naming conventions need to be observed.

The following example illustrates the scenario of a Project Model that executes a Patch method to update a specific Project.

The following files are involved in this:

  • the route calling the updateProject method on the ProjectsController
  • the Controller named ProjectsController
  • the Model called Project
  • the Form Request called UpdateProjectRequest
  • the Resource called UpdateProjectResource

Notice that, in one form or another, the Controller and Method names are used to set the standard for all files involved.

Digging a bit deeper on this standard we have the following:

  • the Resource uses the method name but in CamelCase (UpdateProject) with a Resource suffix and is stored in a folder with the Model name (\app\Http\Resources\Project\UpdateProjectResource.php)

The Route

The Route configuration is tha basic standard for Laravel. You point it to a specific Controller and the method it will invoke.

Each named parameter in the Route will be sent to the method in the order that they are found and with the Request data sent as the last parameter. In the example below the updateProject method will receive as first parameter the projectUuid, the second will be anotherParameter and the third will be the validated Response data.

Route::patch(
	'/project/{projectUuid}/abcd/{anotherParameter}',
	[
		App\Http\Controllers\Api\v1\ProjectsController::class,
		'updateProject'
	]
)->name('project.updateProject');

The Controller

The Controller should use standard Laravel naming convention of a plural entity name (Projects) and be saved in the regular Http folder. Since this is an API-only project it is stored inside a versioned API folder.

It should also extend \Dotcubed\LaravelRadMvc\Controllers\ApiController so that any methods that are not on the Controller be parsed through to the Model.

\app\Http\Api\v1\Controllers\ProjectsController.php

<?php
namespace App\Http\Controllers\Api\v1;

class ProjectsController extends \Dotcubed\LaravelRadMvc\Controllers\ApiController
{
}

The Model

The Model should use the Laravel naming standard where the entity is in its singular form (Project) and be saved in the Models folder (\app\Models\Project.php). The method that will be triggered should be static and with the exact name that the Route points to. If the model returns actual data this data will then be parsed through the Resource, if there is one.

app\Https\Models\Project.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Project extends Model
{
	/**
	* @param array<string, string> $projectData
	*/
	public static function updateProject(
		string $projectUuid,
		string $anotherParameter,
		array $data
	): Project {
		$project = self::where('uuid', $projectUuid)->firstOrFail();
		$project->update($data);

		...
	
		//here as example to showcase the Resource functionality
		return $project;
	}

The Form Request

The Form Request is optional and, if present, will be used to validate the data coming from the Request.

The Form Request should use the following naming convention:

  • app\Http\Requests\

It should also extend the custom AbstractRequest from this library. This way the response can be dully formatted in specific response cases like 422.

Take notice that the method name used in the file name is in CamelCase to avoid changing file naming conventions within Laravel. This means that the file name will not be updateProjectRequest.php but instead UpdateProjectRequest.php.

\app\Http\Requests\Project\UpdateProjectRequest.php

<?php

namespace App\Http\Requests\Project;

use App\Http\Requests\AbstractRequest;
use Illuminate\Validation\Rule;

class UpdateProjectRequest extends AbstractRequest
{
	/**
	* Determine if the user is authorized to make this request.
	*/
	public function authorize(): bool
	{
		return  true;
	}
 
	/**
	* Get the validation rules that apply to the request.
	*
	* @return  array<string, array<int, string>>
	*/
	public function rules(): array
	{
		return [
			'name' => [
				'required',
				'string',
			],
			'description' => [
				'required',
				'string',
			],
		];
	}
}

The Resource

The Resource, if present, will be used to parse the returned data from Model method and prepare it for a JSON Reponse. If the Resource is not present then the data sent back from the Model method will be sent to the Response as-is.

The Form Request uses the following naming convention:

  • app\Http\Resources\

Just like the Form Request, the Resource will use CamelCase for its naming convention. This means that the file name will not be updateProjectResource.php but instead UpdateProjectResource.php.

\app\Http\Resources\Project\UpdateProjectRequest.php

<?php

namespace App\Http\Resources\Project;

use Illuminate\Http\Resources\Json\JsonResource;

/**
* @mixin \App\Models\Project
*/
class GetProjectResource extends JsonResource
{
	/**
	* Transform the resource into an array.
	*
	* @return  array<string, mixed>
	*/
	public  function toArray($request): array
	{
		return [
			'uuid' => $this->uuid,
			'name' => $this->name,
			'description' => $this->description,
		];
	}
}

Code flow

The logic under the hood for this library is the following for the given example:

  • if the method updateProject exists on the Project Controller it will hand over the execution to the Controller method and the regular workflow of the Request will run
  • if the method updateProject does not exist in the Project Controller then it will:
    • infers a Model singular name based on the Controller plural name used in the Route. eg.:
      • if a Controller is named ProjectsController then it will infer that the Model name is Project (singular)
      • a Controller named UserCheckBookController will infer that the Model name is UserCheckBook
    • checks if there is an actual Model named as the inferred name located at \app\Models\Project.php.
    • checks if there is a method called updateProject in this Model
    • checks if there is a FormRequest named \app\Http\Requests\Project\UpdateProjectRequest.php
      • if the Form Request exists then it triggers the Request validation using this Form Request
    • triggers the method updateProject in the Project Model and stores its return (if any)
    • checks if there is a Resource located at \app\Http\Resources\Project\UpdateProjectResource.php
      • if the Resource exists then it pipes the return from Model method into this Resource
    • response is returned using the standard Response format

Logging

Every step of the logic used to fetch, verify, and trigger all elements involved is logged to the default Log channel as INFO log records.