Skip to main content

Routes & Controllers

Routes define the URLs your addon responds to, while controllers handle the logic for those requests. This guide covers routing patterns and controller best practices.

Route Files

Addons have two main route files:

FilePurposeMiddleware
Routes/web.phpWeb routes with sessionsweb
Routes/api.phpStateless API routesapi

Basic Web Routes

Simple Routes

// Routes/web.php
<?php

use Illuminate\Support\Facades\Route;
use Addons\EmailValidator\Http\Controllers\ValidatorController;

Route::middleware(['web', 'auth'])->prefix('email-validator')->group(function () {

// Basic routes
Route::get('/', [ValidatorController::class, 'index'])
->name('emailvalidator.index');

Route::get('/validate', [ValidatorController::class, 'showValidateForm'])
->name('emailvalidator.validate.form');

Route::post('/validate', [ValidatorController::class, 'validate'])
->name('emailvalidator.validate');

Route::get('/history', [ValidatorController::class, 'history'])
->name('emailvalidator.history');

Route::get('/settings', [ValidatorController::class, 'settings'])
->name('emailvalidator.settings');

Route::post('/settings', [ValidatorController::class, 'saveSettings'])
->name('emailvalidator.settings.save');
});

Resource Routes

For CRUD operations, use resource routes:

use Addons\EmailValidator\Http\Controllers\RuleController;

Route::middleware(['web', 'auth'])->prefix('email-validator')->group(function () {

// Creates: index, create, store, show, edit, update, destroy
Route::resource('rules', RuleController::class)
->names([
'index' => 'emailvalidator.rules.index',
'create' => 'emailvalidator.rules.create',
'store' => 'emailvalidator.rules.store',
'show' => 'emailvalidator.rules.show',
'edit' => 'emailvalidator.rules.edit',
'update' => 'emailvalidator.rules.update',
'destroy' => 'emailvalidator.rules.destroy',
]);
});

Route Parameters

// Single parameter
Route::get('/result/{id}', [ValidatorController::class, 'showResult'])
->name('emailvalidator.result');

// Multiple parameters
Route::get('/history/{year}/{month}', [ValidatorController::class, 'historyByMonth'])
->name('emailvalidator.history.month');

// Optional parameters
Route::get('/export/{format?}', [ValidatorController::class, 'export'])
->name('emailvalidator.export');

// Parameter constraints
Route::get('/result/{id}', [ValidatorController::class, 'showResult'])
->where('id', '[0-9]+')
->name('emailvalidator.result');

API Routes

RESTful API

// Routes/api.php
<?php

use Illuminate\Support\Facades\Route;
use Addons\EmailValidator\Http\Controllers\Api\ValidatorApiController;

Route::middleware(['api', 'auth:api'])->prefix('email-validator')->group(function () {

// Validate single email
Route::post('/validate', [ValidatorApiController::class, 'validate']);

// Batch validation
Route::post('/validate/batch', [ValidatorApiController::class, 'validateBatch']);

// Get validation result
Route::get('/result/{id}', [ValidatorApiController::class, 'getResult']);

// List validation history
Route::get('/history', [ValidatorApiController::class, 'history']);

// Webhook endpoints (no auth)
Route::post('/webhook/callback', [ValidatorApiController::class, 'webhookCallback'])
->withoutMiddleware(['auth:api']);
});

API Versioning

Route::middleware(['api', 'auth:api'])->prefix('v1/email-validator')->group(function () {
Route::post('/validate', [ValidatorApiController::class, 'validate']);
});

Route::middleware(['api', 'auth:api'])->prefix('v2/email-validator')->group(function () {
Route::post('/validate', [ValidatorApiV2Controller::class, 'validate']);
});

Middleware

Available Middleware

MiddlewarePurpose
webSession, cookies, CSRF protection
authRequires authenticated user
2faTwo-factor authentication check
langLocalization
adminAdmin role required

Applying Middleware

// Group middleware
Route::middleware(['web', 'auth', '2fa'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
});

// Single route middleware
Route::get('/admin-only', [AdminController::class, 'index'])
->middleware(['auth', 'admin']);

// Controller middleware (in constructor)
public function __construct()
{
$this->middleware(['auth', '2fa']);
$this->middleware('admin')->only(['destroy', 'forceDelete']);
}

Custom Middleware

Create addon-specific middleware:

// Http/Middleware/CheckQuota.php
namespace Addons\EmailValidator\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class CheckQuota
{
public function handle(Request $request, Closure $next)
{
$user = auth()->user();
$dailyLimit = $user->package->email_validation_limit ?? 100;
$usedToday = $this->getTodayUsage($user->id);

if ($usedToday >= $dailyLimit) {
return response()->json([
'error' => 'Daily validation quota exceeded',
'limit' => $dailyLimit,
'used' => $usedToday
], 429);
}

return $next($request);
}

private function getTodayUsage(int $userId): int
{
// Query usage from database
}
}

Register in service provider:

public function boot(): void
{
$this->app['router']->aliasMiddleware(
'check.quota',
\Addons\EmailValidator\Http\Middleware\CheckQuota::class
);
}

Use in routes:

Route::middleware(['auth', 'check.quota'])->group(function () {
Route::post('/validate', [ValidatorController::class, 'validate']);
});

Controllers

Basic Controller

// Http/Controllers/ValidatorController.php
<?php

namespace Addons\EmailValidator\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Addons\EmailValidator\Entities\ValidationResult;

class ValidatorController extends Controller
{
public function __construct()
{
$this->middleware(['auth', '2fa']);
}

public function index()
{
$stats = [
'total_validated' => ValidationResult::where('user_id', auth()->id())->count(),
'valid_emails' => ValidationResult::where('user_id', auth()->id())
->where('status', 'valid')->count(),
'invalid_emails' => ValidationResult::where('user_id', auth()->id())
->where('status', 'invalid')->count(),
];

return view('emailvalidator::index', compact('stats'));
}

public function showValidateForm()
{
return view('emailvalidator::validate');
}

public function validate(Request $request)
{
$request->validate([
'emails' => 'required|string',
]);

$emails = array_filter(
array_map('trim', explode("\n", $request->emails))
);

// Process validation
$results = $this->processValidation($emails);

return view('emailvalidator::results', compact('results'));
}

public function history()
{
$results = ValidationResult::where('user_id', auth()->id())
->orderBy('created_at', 'desc')
->paginate(50);

return view('emailvalidator::history', compact('results'));
}

private function processValidation(array $emails): array
{
// Validation logic
}
}

Resource Controller

// Http/Controllers/RuleController.php
<?php

namespace Addons\EmailValidator\Http\Controllers;

use App\Http\Controllers\Controller;
use Addons\EmailValidator\Entities\ValidationRule;
use Addons\EmailValidator\Http\Requests\StoreRuleRequest;
use Addons\EmailValidator\Http\Requests\UpdateRuleRequest;

class RuleController extends Controller
{
public function index()
{
$rules = ValidationRule::where('user_id', auth()->id())
->orderBy('priority')
->get();

return view('emailvalidator::rules.index', compact('rules'));
}

public function create()
{
return view('emailvalidator::rules.create');
}

public function store(StoreRuleRequest $request)
{
ValidationRule::create([
'user_id' => auth()->id(),
'name' => $request->name,
'pattern' => $request->pattern,
'action' => $request->action,
'priority' => $request->priority ?? 0,
]);

return redirect()
->route('emailvalidator.rules.index')
->with('success', 'Rule created successfully');
}

public function show(ValidationRule $rule)
{
$this->authorize('view', $rule);

return view('emailvalidator::rules.show', compact('rule'));
}

public function edit(ValidationRule $rule)
{
$this->authorize('update', $rule);

return view('emailvalidator::rules.edit', compact('rule'));
}

public function update(UpdateRuleRequest $request, ValidationRule $rule)
{
$this->authorize('update', $rule);

$rule->update($request->validated());

return redirect()
->route('emailvalidator.rules.index')
->with('success', 'Rule updated successfully');
}

public function destroy(ValidationRule $rule)
{
$this->authorize('delete', $rule);

$rule->delete();

return redirect()
->route('emailvalidator.rules.index')
->with('success', 'Rule deleted successfully');
}
}

API Controller

// Http/Controllers/Api/ValidatorApiController.php
<?php

namespace Addons\EmailValidator\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;

class ValidatorApiController extends Controller
{
public function validate(Request $request): JsonResponse
{
$request->validate([
'email' => 'required|email',
]);

$result = $this->validateEmail($request->email);

return response()->json([
'success' => true,
'data' => $result
]);
}

public function validateBatch(Request $request): JsonResponse
{
$request->validate([
'emails' => 'required|array|max:100',
'emails.*' => 'required|email',
]);

$results = [];
foreach ($request->emails as $email) {
$results[] = $this->validateEmail($email);
}

return response()->json([
'success' => true,
'data' => $results,
'total' => count($results)
]);
}

public function getResult(int $id): JsonResponse
{
$result = ValidationResult::where('id', $id)
->where('user_id', auth()->id())
->firstOrFail();

return response()->json([
'success' => true,
'data' => $result
]);
}

public function history(Request $request): JsonResponse
{
$results = ValidationResult::where('user_id', auth()->id())
->orderBy('created_at', 'desc')
->paginate($request->per_page ?? 20);

return response()->json([
'success' => true,
'data' => $results->items(),
'meta' => [
'current_page' => $results->currentPage(),
'last_page' => $results->lastPage(),
'per_page' => $results->perPage(),
'total' => $results->total(),
]
]);
}

private function validateEmail(string $email): array
{
// Validation logic
}
}

Form Requests

Use form requests for validation:

// Http/Requests/StoreRuleRequest.php
<?php

namespace Addons\EmailValidator\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreRuleRequest extends FormRequest
{
public function authorize(): bool
{
return auth()->check();
}

public function rules(): array
{
return [
'name' => 'required|string|max:255',
'pattern' => 'required|string|max:500',
'action' => 'required|in:accept,reject,flag',
'priority' => 'nullable|integer|min:0|max:100',
];
}

public function messages(): array
{
return [
'name.required' => 'Please provide a name for this rule',
'pattern.required' => 'A regex pattern is required',
'action.in' => 'Invalid action selected',
];
}
}

Route Service Provider

The RouteServiceProvider loads your route files:

// Providers/RouteServiceProvider.php
<?php

namespace Addons\EmailValidator\Providers;

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

class RouteServiceProvider extends ServiceProvider
{
protected string $moduleNamespace = 'Addons\EmailValidator\Http\Controllers';

public function boot(): void
{
parent::boot();
}

public function map(): void
{
$this->mapWebRoutes();
$this->mapApiRoutes();
}

protected function mapWebRoutes(): void
{
Route::middleware('web')
->namespace($this->moduleNamespace)
->group(module_path('EmailValidator', '/Routes/web.php'));
}

protected function mapApiRoutes(): void
{
Route::prefix('api')
->middleware('api')
->namespace($this->moduleNamespace . '\Api')
->group(module_path('EmailValidator', '/Routes/api.php'));
}
}

Role-Based Routing

Load different routes based on user role:

protected function mapWebRoutes(): void
{
Route::middleware('web')->group(function () {
// Common routes for all authenticated users
Route::middleware('auth')
->namespace($this->moduleNamespace)
->group(module_path('EmailValidator', '/Routes/common.php'));

// Admin-only routes
if ($this->isAdmin()) {
Route::middleware(['auth', 'admin'])
->namespace($this->moduleNamespace)
->group(module_path('EmailValidator', '/Routes/admin.php'));
}

// Client routes
if ($this->isClient()) {
Route::middleware(['auth'])
->namespace($this->moduleNamespace)
->group(module_path('EmailValidator', '/Routes/client.php'));
}
});
}

private function isAdmin(): bool
{
if (!auth()->check()) return false;
return auth()->user()->role_id === 1;
}

private function isClient(): bool
{
if (!auth()->check()) return false;
return auth()->user()->is_client === 1;
}

Best Practices

Route Naming

Use consistent, namespaced route names:

// Good
Route::get('/settings', ...)->name('emailvalidator.settings');
Route::get('/rules/{rule}', ...)->name('emailvalidator.rules.show');

// Bad
Route::get('/settings', ...)->name('settings');
Route::get('/rules/{rule}', ...)->name('showRule');

Controller Organization

  • One controller per resource or feature area
  • Keep controllers thin - move logic to services
  • Use form requests for validation
  • Use policies for authorization

Response Consistency

For API responses, use a consistent format:

// Success
return response()->json([
'success' => true,
'data' => $result,
'message' => 'Operation completed'
]);

// Error
return response()->json([
'success' => false,
'error' => 'Validation failed',
'errors' => $validator->errors()
], 422);