# Route Management Module

Module untuk auto-generate dan manage application routes, inspired by Yii2's mdm/mimin RBAC route system.

## Overview

Module ini membolehkan admin scan semua registered routes dari Laravel, generate ke database, dan gunakannya sebagai menu link resources.

## Structure

```
app/
├── Models/Backend/
│   └── Route.php                    # Eloquent model
├── Services/
│   ├── RouteScanner.php             # Scan registered routes
│   └── ControllerScanner.php        # Scan controller methods
└── Http/Controllers/Backend/
    └── RouteController.php          # CRUD + generate/sync

database/migrations/
└── 2026_05_17_000001_create_routes_table.php

resources/views/backend/module/routes/
├── index.blade.php                  # List routes
├── create.blade.php                 # Add route
├── edit.blade.php                   # Edit route
└── available.blade.php              # Compare registered vs available
```

## Database Schema

### routes table

| Column      | Type          | Description                    |
|-------------|---------------|--------------------------------|
| id          | bigint        | Primary key                    |
| name        | varchar(255)  | Route name (e.g. `ref.index`) |
| uri         | varchar(255)  | URI path (e.g. `/admin/ref`)   |
| method      | varchar(50)   | HTTP method (GET, POST, etc)   |
| controller  | varchar(255)  | Full controller class name     |
| action      | varchar(255)  | Method name in controller      |
| middleware   | varchar(255)  | Middleware stack              |
| type        | varchar(20)   | web / api / admin             |
| status      | boolean       | Active/Inactive               |
| alias       | varchar(255)  | Display alias                 |
| created_at  | timestamp     |                               |
| updated_at  | timestamp     |                               |
| deleted_at  | timestamp     | Soft deletes                  |

## Models

### Route Model (`app/Models/Backend/Route.php`)

**Attributes (Fillable):**
- `name` - Route name (nullable)
- `uri` - URI path
- `method` - HTTP method
- `controller` - Controller class
- `action` - Action method
- `middleware` - Middleware
- `type` - web/api/admin
- `status` - boolean
- `alias` - Display name

**Static Methods:**
- `Route::rules($id)` - Validation rules
- `Route::methodColors()` - Tailadmin badge colors for HTTP methods
- `Route::typeColors()` - Badge colors for route types

**Scopes:**
- `Route::web()` - Filter web routes
- `Route::api()` - Filter api routes
- `Route::admin()` - Filter admin routes
- `Route::active()` - Filter active routes
- `Route::inactive()` - Filter inactive routes

## Services

### RouteScanner (`app/Services/RouteScanner.php`)

Scan semua registered routes dari `Route::getRoutes()`.

**Methods:**
- `scan()` - Return array semua routes
- `scanAndCache()` - Scan + cache untuk 1 hari
- `getCached()` - Get dari cache
- `clearCache()` - Clear cache
- `refresh()` - Clear + rescan

**Extracted Data:**
```php
[
    'name' => 'route.name',
    'uri' => '/admin/users',
    'method' => 'GET',
    'controller' => 'App\\Http\\Controllers\\Backend\\UserController',
    'action' => 'index',
    'middleware' => 'auth,admin',
    'type' => 'admin',
]
```

**Filters (skipped):**
- Closure routes
- Debug routes (`_debugbar`, `telescope`, `horizon`)
- Vendor routes
- Sanctum/Passport routes

### ControllerScanner (`app/Services/ControllerScanner.php`)

Scan semua controllers dalam `app/Http/Controllers/`.

**Methods:**
- `scan()` - Recursively scan controllers
- `scanAndCache()` - Scan + cache
- `getCached()` - Get from cache
- `clearCache()` - Clear cache

**Logic:**
1. Find semua file `*Controller.php`
2. Use ReflectionClass untuk get public methods
3. Skip constructor, middleware, magic methods
4. Generate URI dari controller name + method (kebab-case)
5. Auto-detect resource methods (index, create, store, show, edit, update, destroy)

## RouteController (`app/Http/Controllers/Backend/RouteController.php`)

### Methods

| Method | Route | Description |
|--------|-------|-------------|
| `index()` | GET /admin/routes | List all routes with search/filter |
| `create()` | GET /admin/routes/create | Show create form |
| `store()` | POST /admin/routes | Save new route |
| `edit($id)` | GET /admin/routes/{id}/edit | Show edit form |
| `update($request, $id)` | PUT /admin/routes/{id} | Update route |
| `destroy($id)` | DELETE /admin/routes/{id} | Delete route |
| `generate($request)` | POST /admin/routes/generate | Scan & create/update routes |
| `sync()` | GET /admin/routes/sync | Sync all routes |
| `refresh()` | GET /admin/routes/refresh | Clear cache & rescan |
| `available()` | GET /admin/routes/available | Show unregistered routes |
| `bulkDelete()` | POST /admin/routes/bulk-delete | Delete multiple routes |
| `bulkStatus()` | POST /admin/routes/bulk-status/{status} | Activate/deactivate multiple |

### Query Parameters (index)

- `search` - Search name, uri, controller, action
- `type` - Filter web/api/admin
- `method` - Filter GET/POST/PUT/PATCH/DELETE
- `status` - Filter active/inactive

## Views

### index.blade.php

Table columns:
- **No** - Row number
- **Route Name** - name field (nullable)
- **URL** - URI + full URL link
- **Method** - HTTP method badge (color coded)
- **Controller** - Controller class + action
- **Type** - web/api/admin badge (color coded)
- **Status** - Active/Inactive badge
- **Actions** - Edit/Delete buttons

Features:
- Search & filter forms
- Generate Routes button (green)
- Sync All button
- Refresh Cache button
- Available Routes button
- Pagination (20 per page)
- Delete confirmation (SweetAlert)

### create.blade.php / edit.blade.php

Form fields:
- URI (text)
- Route Name (text)
- HTTP Method (dropdown)
- Type (dropdown: web/api/admin)
- Status (dropdown: Active/Inactive)
- Controller (text)
- Action (text)
- Middleware (text)
- Alias (text)

### available.blade.php

Two columns comparison:
- **Not Yet Registered** - Routes scanned but not in DB (red theme)
- **Already Registered** - Routes in DB (green theme)

Generate button to add missing routes.

## Usage Flow

### 1. Generate Routes (First Time)

```
Admin Panel > Routes > Click "Generate Routes"
```

System will:
1. Scan all registered routes from `Route::getRoutes()`
2. Scan all controller methods
3. Insert new routes to database
4. Update existing routes if changed
5. Show success message with count

### 2. Manage Routes

- **Add Manually** - Click "Add Route" button
- **Edit** - Click edit icon on row
- **Delete** - Click delete icon (with confirmation)
- **Bulk Delete** - Select checkboxes > Delete Selected
- **Bulk Activate/Deactivate** - Select > click activate/deactivate buttons

### 3. Use in Menu

In Backend Menu / Frontend Menu management:
1. Create new menu item
2. Set `menu_link` to route name (e.g. `admin.routes.index`)
3. Or set to full URI (e.g. `/admin/routes`)
4. Set `menu_active_route` for active state detection

### 4. Sync Routes (Regularly)

Click "Sync All" to:
1. Scan for new routes
2. Add missing routes to DB
3. Remove stale routes (routes starting with `generated_` that no longer exist)

## Permissions

Required permissions for routes module:
- `route.view` - View routes list
- `route.create` - Create/Generate routes
- `route.update` - Edit routes
- `route.delete` - Delete routes

These permissions are added in `BackendMenuSeeder`.

## Route Type Detection

Type is auto-detected based on:
- URI prefix (`api/`, `admin/`)
- Middleware (`api`, `admin`)

Default: `web`

## HTTP Method Badge Colors

| Method | Color | Tailadmin Class |
|--------|-------|-----------------|
| GET | Brand | `bg-brand-50 text-brand-600` |
| POST | Success (Green) | `bg-success-50 text-success-600` |
| PUT | Warning (Yellow) | `bg-warning-50 text-warning-600` |
| PATCH | Warning (Yellow) | `bg-warning-50 text-warning-600` |
| DELETE | Error (Red) | `bg-error-50 text-error-600` |
| OPTIONS | Gray | `bg-gray-50 text-gray-600` |
| HEAD | Brand | `bg-brand-50 text-brand-600` |

## Route Type Badge Colors

| Type | Color | Tailadmin Class |
|------|-------|-----------------|
| web | Brand | `bg-brand-50 text-brand-600` |
| api | Purple | `bg-purple-50 text-purple-600` |
| admin | Pink | `bg-pink-50 text-pink-600` |

## Caching

Services use Laravel cache:
- Key: `scanned_routes` (RouteScanner)
- Key: `scanned_controllers` (ControllerScanner)
- TTL: 1 day

Clear cache via:
- "Refresh Cache" button
- `RouteScanner::clearCache()`
- `ControllerScanner::clearCache()`

## Notes

- Routes with `name` can be used with `route()` helper
- Routes without `name` use URI directly
- Soft deletes enabled - deleted routes can be restored
- Method column accepts comma-separated for multiple methods (e.g. `GET,POST`)
- URI uses `/` prefix convention

## Usage Examples

### 1. Get All Active Routes

```php
use App\Models\Backend\Route;

$routes = Route::active()->get();

foreach ($routes as $route) {
    echo $route->name . ' -> ' . $route->uri;
}
```

### 2. Get Routes by Type

```php
// Admin routes only
$adminRoutes = Route::active()->admin()->get();

// Web routes only
$webRoutes = Route::active()->web()->get();

// API routes only
$apiRoutes = Route::active()->api()->get();
```

### 3. Get Routes by HTTP Method

```php
$getRoutes = Route::active()->where('method', 'GET')->get();
$postRoutes = Route::active()->where('method', 'POST')->get();
```

### 4. Search Routes

```php
// In controller
$routes = Route::where('name', 'like', '%ref%')
    ->orWhere('uri', 'like', '%ref%')
    ->active()
    ->get();
```

### 5. Use in Menu Link Selection

```php
// Get all active routes for dropdown
$routeOptions = Route::active()
    ->orderBy('uri')
    ->pluck('name', 'uri')
    ->toArray();

// Or with route name as key
$routeOptions = Route::active()
    ->whereNotNull('name')
    ->pluck('name', 'name')
    ->toArray();
```

### 6. Check if Route Exists

```php
use App\Models\Backend\Route;

$exists = Route::where('uri', '/admin/ref')->exists();

if ($exists) {
    echo "Route exists";
}
```

### 7. Get Single Route

```php
$route = Route::where('name', 'ref.index')->first();

if ($route) {
    echo $route->uri; // /admin/ref
    echo route($route->name); // Full URL
}
```

### 8. Use Route Name for route() Helper

In views or controllers:
```php
// If route has name stored
{{ route('ref.index') }}

// If using URI directly
{{ url('/admin/ref') }}
```

### 9. Generate Route Link in Menu

```php
// In your menu rendering logic
$route = Route::where('menu_link', $menuItem->menu_link)->first();

if ($route) {
    $link = $route->name ? route($route->name) : url($route->uri);
} else {
    $link = url($menuItem->menu_link);
}
```

### 10. Bulk Update Route Status

```php
// Deactivate all admin routes
Route::where('type', 'admin')->update(['status' => false]);

// Activate specific routes
$routeIds = [1, 2, 3];
Route::whereIn('id', $routeIds)->update(['status' => true]);
```

## Where It's Used

### 1. BackendMenuSeeder

Routes are registered as menu items:
```php
$this->menu('Routes', 'admin.routes.index', null, ['route.view', 'route.create', 'route.update', 'route.delete'], $backend->menu_id);
```

Permissions added:
- `route.view`
- `route.create`
- `route.update`
- `route.delete`

### 2. Menu Link Selection

In Backend Menu / Frontend Menu management forms, routes can be selected as `menu_link` values:

```php
// In your menu form view
<select name="menu_link">
    <option value="">-- Select Route --</option>
    @foreach(\App\Models\Backend\Route::active()->get() as $route)
        <option value="{{ $route->name ?? $route->uri }}">
            {{ $route->name ?? $route->uri }} ({{ $route->method }})
        </option>
    @endforeach
</select>
```

### 3. Active Route Detection

In layouts or menu partials:
```php
@php
use App\Models\Backend\Route;

$activeRoutes = Route::active()
    ->where('menu_active_route', 'like', '%' . Request::route()->getName() . '%')
    ->first();
@endphp

@if($activeRoutes)
    <li class="mm-active">...</li>
@endif
```

### 4. Permission Generation

In AutoPermissionController or similar:
```php
// Generate permissions from routes
foreach (Route::active()->get() as $route) {
    $permission = $route->name;
    if ($permission) {
        Permission::findOrCreate($permission, 'admin');
    }
}
```

### 5. Route Resource for Menu

```php
// Example: Create menu link for content-photo-list
$route = Route::where('name', 'content-photo-list.index')->first();

// Result:
// name: content-photo-list.index
// uri: /admin/content-photo-list
// method: GET
// type: admin
// controller: App\Http\Controllers\Backend\ContentPhotoListController
// action: index
```

### 6. API Response for Frontend

```php
// Return routes as JSON for frontend
Route::active()
    ->select('name', 'uri', 'method', 'type')
    ->orderBy('uri')
    ->get();

// Output:
[
    {"name":"ref.index","uri":"/admin/ref","method":"GET","type":"admin"},
    {"name":"ref.create","uri":"/admin/ref/create","method":"GET","type":"admin"},
    ...
]
```

## Integration with Other Modules

### 1. BackendMenu (menu link storage)

The `menu_link` field stores route names:
```php
$menu->menu_link = 'content-photo-list.index';
$menu->menu_active_route = ['content-photo-list.index', 'content-photo-list.create'];
```

### 2. RolePermission (permission assignment)

Permissions derived from route names:
```php
// Permission: content-photo-list.index
// Can be assigned to roles via Spatie
$role->givePermissionTo('content-photo-list.index');
```

### 3. Activity Logging

Route model uses SoftDeletes for audit trail.

## Common Issues

### 1. Route Not Found in Menu

Check that:
- Route exists in database (`Route::where('name', 'ref.index')->exists()`)
- Route has `status = true`
- Route name matches exactly (case-sensitive)

### 2. URL Not Generating

```php
$route = Route::where('name', 'ref.index')->first();

if ($route && $route->name) {
    echo route($route->name); // Works
} else {
    echo url($route->uri); // Fallback
}
```

### 3. Sync Not Finding Routes

Run "Refresh Cache" first:
```php
$scanner = app(\App\Services\RouteScanner::class);
$scanner->refresh();
```

### 4. Method Column Too Short

The `method` column accepts multiple methods (comma-separated). Current max is `VARCHAR(50)`, sufficient for all cases.

### 5. Edit Form Shows Empty / Create Form Instead of Edit Data

**Problem:**
When clicking edit on a route, the form shows empty fields or create form instead of existing data.

**Why This Happens:**

Laravel Route Model Binding conflict. The `Route` model in this module has name conflict with Laravel's built-in `Illuminate\Routing\Route` class.

When using type hint `Route $route` in controller methods:
```php
public function edit(Route $route)
```

Laravel's router tries to resolve the `Route` type hint using its own Route class (which represents URL routes, not the database model), not our `App\Models\Backend\Route` model.

This causes:
1. Form shows empty or create form
2. `$route->id` returns null or wrong value
3. `route('admin.routes.update', ['id' => $route->id])` fails with "Missing required parameter"

**Solution:**

Use explicit ID parameter and find model manually instead of Route Model Binding:

```php
// WRONG - causes conflict
public function edit(Route $route)
{
    return view('...', ['route' => $route]);
}

// CORRECT - explicit find
public function edit($id)
{
    $route = Route::findOrFail($id);
    return view('...', ['route' => $route]);
}
```

**Controller Methods Updated:**

```php
public function edit($id)
{
    $route = Route::findOrFail($id);
    // ...
}

public function update(Request $request, $id)
{
    $route = Route::findOrFail($id);
    // ...
}

public function destroy($id)
{
    $route = Route::findOrFail($id);
    // ...
}
```

**Why Use `url()` Helper Instead of `route()` in Views:**

In edit.blade.php, use `url()` helper instead of `route()` to avoid parameter issues:
```php
// Instead of route() which may have parameter issues
<form action="{{ url('/admin/routes/' . $route->id) }}" method="POST">

// Or use route() with explicit array syntax
<form action="{{ route('admin.routes.update', ['id' => $route->id]) }}" method="POST">
```

### 6. URL Generation Error - Missing Parameter

**Error:**
```
UrlGenerationException: Missing required parameter for [Route: admin.routes.update] [URI: admin/routes/{id}] [Missing parameter: id]
```

**Cause:**
When using `route('admin.routes.update', $route->id)` without explicit key, Laravel may not correctly pass the parameter.

**Fix:**
Always use explicit key in array:
```php
route('admin.routes.update', ['id' => $route->id])
```

Or use `url()` helper:
```php
url('/admin/routes/' . $route->id)
```