Yurich
Posted on March 17, 2024
Let's create Laravel app
composer create-project laravel/laravel example-app
or laravel new example-app
go to app folder cd example-app
Now you can run php artisan serve
and visit http://127.0.0.1:8000 to verify that the website is working.
Remember that we need to adjust the .env
file, specifically to configure the database connection and specify the correct URL APP_URL=http://127.0.0.1:8000
.
If we don't have API routes it can be easily fixed by running the command php artisan install:api
.
Let's proceed to create a modular structure. The modules will be located in the Modules
folder, so let's create them in the app
directory.
First of all, let's dynamically include all routes. To do this, go to app.php
and add the following code:
use Illuminate\Support\Facades\Route;
if (!defined('API_PREFIX')) define('API_PREFIX', 'api/v1');
$modules_folder = app_path('Modules');
$modules = array_values(
array_filter(
scandir($modules_folder),
function ($item) use ($modules_folder) {
return is_dir($modules_folder.DIRECTORY_SEPARATOR.$item) && ! in_array($item, ['.', '..']);
}
)
);
foreach ($modules as $module) {
$routesPath = $modules_folder.DIRECTORY_SEPARATOR.$module.DIRECTORY_SEPARATOR.'routes_api.php';
if (file_exists($routesPath)) {
Route::prefix(API_PREFIX)
->middleware(['auth:sanctum'])
->namespace("\\App\\Modules\\$module\Controllers")
->group($routesPath);
}
}
To make the SPA works, you need to add one more route at the end of the web.php
file.
Route::view('/{any}', 'spa')->where('any', '^(?!api).*');
We also need to create a Blade template spa.blade.php
for the main route where Vue will be connected with libraries.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name') }}</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
Now, all routes that do not start with /api
will be directed to this template. This will enable front-end routing.
Building a Restful API
Following the modular system laid out in the article My point of view on SPA modularity using Laravel and Vue.js, let's create the first module.
To begin, let's create a folder app\Modules
.
Let's assume our CRM will have a system for managing static pages, meaning we need CRUD functionality for this.
Execute the command to create a model and migration
php artisan make:model Page -m
Edit the created files
Add the name
field to both the migration file and the model. The code for the files will look accordingly.
Migration:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations. * * @return void
*/
public function up()
{
Schema::create('pages', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('pages');
}
};
Model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Page extends Model
{
use HasFactory;
const COLUMN_ID = 'id';
const COLUMN_NAME = 'name';
protected $guarded = [self::COLUMN_ID];
}
Let's run the migrations: php artisan migrate
.
In the Modules
directory, create a folder named Page
, which will contain the following files:
routes_api.php
<?php
use Illuminate\Support\Facades\Route;
Route::apiResource('pages', 'PageController');
Controllers/PageController.php
<?php
namespace App\Modules\Page\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Page;
use App\Modules\Page\Requests\PageRequest;
use App\Modules\Page\Resources\PageResource;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
class PageController extends Controller
{
/**
* Display list of resources
*
* @param Request $request
* @return AnonymousResourceCollection
*/
public function index(Request $request)
{
[$column, $order] = explode(',', $request->input('sortBy', 'id,asc'));
$pageSize = (int) $request->input('pageSize', 10);
$resource = Page::query()
->when($request->filled('search'), function (Builder $q) use ($request) {
$q->where(Page::COLUMN_NAME, 'like', '%'.$request->search.'%');
})
->orderBy($column, $order)->paginate($pageSize);
return PageResource::collection($resource);
}
/**
* Store a newly created resource in storage.
*
* @param PageRequest $request
* @param Page $page
* @return JsonResponse
*/
public function store(PageRequest $request, Page $page)
{
$data = $request->validated();
$page->fill($data)->save();
return response()->json([
'type' => self::RESPONSE_TYPE_SUCCESS,
'message' => 'Successfully created',
]);
}
/**
* Display the specified resource.
*
* @param Page $page
* @return PageResource
*/
public function show(Page $page)
{
return new PageResource($page);
}
/**
* Update the specified resource in storage.
*
* @param PageRequest $request
* @param Page $page
* @return JsonResponse
*/
public function update(PageRequest $request, Page $page)
{
$data = $request->validated();
$page->fill($data)->save();
return response()->json([
'type' => self::RESPONSE_TYPE_SUCCESS,
'message' => 'Successfully updated',
]);
}
/**
* Delete the specified resource.
*
* @param Page $page
* @return JsonResponse
*
* @throws Exception
*/
public function destroy(Page $page)
{
$page->delete();
return response()->json([
'type' => self::RESPONSE_TYPE_SUCCESS,
'message' => 'Successfully deleted',
]);
}
}
It would be convenient to add constants in App\Http\Controllers\Controller
so they can be used for response status.
const RESPONSE_TYPE_SUCCESS = 'success';
const RESPONSE_TYPE_INFO = 'info';
const RESPONSE_TYPE_WARNING = 'warning';
const RESPONSE_TYPE_ERROR = 'error';
Requests/PageRequest.php
<?php
namespace App\Modules\Page\Requests;
use App\Models\Page;
use Illuminate\Foundation\Http\FormRequest;
class PageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
Page::COLUMN_NAME => 'required|string',
];
}
}
Resources/PageResource.php
<?php
namespace App\Modules\Page\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class PageResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param Request $request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}
Currently, this class doesn't provide much assistance, but it will set a standard response and unload the controller in the future.
Our Restful API for managing the Page entity is ready.
The list of routes for this module you can find running this command
php artisan route:list --path=pages
If you comment out the line ->middleware(['auth:sanctum'])
in the api.php
file, you can easily test this functionality using Postman.
Posted on March 17, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.