Skip to main content

AdminController Extension Guide

Overview

The AdminController is a powerful base controller that provides CRUD operations, menu management, and standardized responses for admin interfaces. This guide covers how to create new controllers that extend AdminController.

Quick Start

Basic Controller Structure

<?php

namespace App\Http\Controllers\YourNamespace;

use App\Http\Controllers\Core\AdminController;
use App\Models\YourNamespace\YourModel;
use Illuminate\Http\Request;

class YourController extends AdminController
{
    public function __construct()
    {
        // Set required properties
        $this->modelClass = YourModel::class;
        $this->entity = 'YourEntity';
        $this->namespace = 'YourNamespace';
        
        // Call parent constructor AFTER setting properties
        parent::__construct();
        
        // Configure controller-specific settings
        $this->setupController();
    }
    
    private function setupController()
    {
        // Configure Inertia page paths
        $this->inertia_page_index = 'YourNamespace/YourEntity/Index';
        $this->inertia_page_create = 'YourNamespace/YourEntity/Create';
        $this->inertia_page_edit = 'YourNamespace/YourEntity/Edit';
        $this->inertia_page_view = 'YourNamespace/YourEntity/View';
        
        // Configure routes
        $this->controller_base_route = 'your-namespace.your-entity';
        $this->controller_index_route = 'your-namespace.your-entity.index';
        
        // Configure search and sort fields
        $this->searchable_fields = ['name', 'email', 'description'];
        $this->sortable_fields = ['created_at', 'updated_at', 'name'];
        
        // Configure validation rules
        $this->setupValidation();
    }
}

Required Properties

Essential Properties (Must Set)

protected string $modelClass = '';     // Fully qualified model class name
protected string $entity = '';         // Entity name for messages/defaults
protected string $namespace = '';      // Controller namespace for organization

Inertia Page Configuration

protected string $inertia_page_index = '';   // e.g., 'Directory/Member/Index'
protected string $inertia_page_create = '';  // e.g., 'Directory/Member/Create'
protected string $inertia_page_edit = '';    // e.g., 'Directory/Member/Edit'
protected string $inertia_page_view = '';    // e.g., 'Directory/Member/View'

Route Configuration

protected string $controller_base_route = '';    // e.g., 'directory.member'
protected string $controller_index_route = '';   // e.g., 'directory.member.index'

Optional Configuration Properties

Search & Filter Configuration

protected array $searchable_fields = [];   // Fields for global search
protected array $sortable_fields = [];     // Fields allowed for sorting
protected array $filter_casts = [];        // Type casting for filters
Example:
$this->searchable_fields = ['name', 'email', 'phone', 'address'];
$this->sortable_fields = ['created_at', 'updated_at', 'name', 'email'];
$this->filter_casts = [
    'status' => 'boolean',
    'age' => 'integer',
    'salary' => 'float'
];

Validation Rules

protected array $validator_create = [];    // Rules for creating records
protected array $validator_update = [];    // Rules for updating records
Example:
private function setupValidation()
{
    $this->validator_create = [
        'name' => 'required|string|max:255|unique:members,name',
        'email' => 'required|email|unique:members,email',
        'phone' => 'nullable|string|max:20',
    ];
    
    $this->validator_update = [
        'name' => 'required|string|max:255|unique:members,name,' . $this->request_id,
        'email' => 'required|email|unique:members,email,' . $this->request_id,
        'phone' => 'nullable|string|max:20',
    ];
}

Pagination & Display

protected int $per_page = 15;                        // Items per page
protected string $data_view_default = 'table';       // Default view type
protected array $extra_props = [];                   // Additional props for views

Authorization

protected string $auth_entity = '';    // Authorization entity name
Example:
$this->auth_entity = 'all-members';

Method Overrides

Query Modification

Override this method to add custom query logic:
public function additionalQuery(Request $request, $query, ?string $type = null)
{
    if ($type === 'index') {
        // Add eager loading for index
        $query->with(['category', 'tags']);
        
        // Add custom filters
        if ($request->filled('category_id')) {
            $query->where('category_id', $request->category_id);
        }
    }
    
    if ($type === 'view' || $type === 'edit') {
        // Add more detailed relationships for single item views
        $query->with(['category', 'tags', 'reviews.user']);
    }
    
    return $query;
}

Page-Specific Customization

public function index(Request $request)
{
    // Set page-specific data
    $this->data_object_extra = [
        'totalActive' => $this->model->where('status', 'active')->count(),
        'statistics' => $this->calculateStatistics(),
    ];
    
    // Set breadcrumbs
    $this->breadcrumbs = [
        ['title' => 'Dashboard', 'href' => route('main.dashboard.index')],
        ['title' => 'Your Section', 'href' => route('your.section.index')],
        ['title' => 'Your Entity List', 'href' => route($this->controller_index_route)],
    ];
    
    return parent::index($request);
}

public function create(Request $request)
{
    // Add form options
    $this->extra_props['categories'] = Category::orderBy('name')->get(['id', 'name']);
    $this->extra_props['statuses'] = ['active', 'inactive', 'pending'];
    
    return parent::create($request);
}

Form Options Setup

public function setupFormOptions(Request $request, $formOptions, $data = null)
{
    // Add dynamic form options based on current data or request
    $formOptions['categories'] = Category::where('active', true)
                                        ->orderBy('name')
                                        ->get(['id', 'name']);
    
    if ($data && isset($data['category_id'])) {
        $formOptions['subcategories'] = Subcategory::where('category_id', $data['category_id'])
                                                   ->get(['id', 'name']);
    }
    
    return $formOptions;
}

Lifecycle Hooks

Create Hooks

protected function beforeStore(Request $request, array $validatedData): array
{
    // Modify data before storing
    $validatedData['user_id'] = auth()->id();
    $validatedData['slug'] = Str::slug($validatedData['name']);
    
    return $validatedData;
}

protected function afterStore(Request $request, Model $instance): void
{
    // Perform actions after storing
    // Log activity
    activity('created')
        ->performedOn($instance)
        ->withProperties($instance->toArray())
        ->log('Created new ' . $this->entity);
    
    // Send notifications
    // Cache clearing
    // etc.
}

Update Hooks

protected function beforeUpdate(Request $request, array $validatedData, Model $instance): array
{
    // Store original data for comparison
    $this->original_data = $instance->getOriginal();
    
    // Modify data before updating
    if (isset($validatedData['name']) && $validatedData['name'] !== $instance->name) {
        $validatedData['slug'] = Str::slug($validatedData['name']);
    }
    
    return $validatedData;
}

protected function afterUpdate(Request $request, Model $instance): void
{
    // Log changes
    $changes = $instance->getChanges();
    if (!empty($changes)) {
        activity('updated')
            ->performedOn($instance)
            ->withProperties([
                'old' => $this->original_data,
                'new' => $changes
            ])
            ->log('Updated ' . $this->entity);
    }
}

Delete Hooks

protected function beforeDestroy(Request $request, Model $instance): void
{
    // Check for dependencies
    if ($instance->children()->count() > 0) {
        throw new \Exception('Cannot delete item with child records');
    }
    
    // Backup data
    $this->backup_data = $instance->toArray();
}

protected function afterDestroy(Request $request, Model $instance): void
{
    // Clean up related data
    // Log deletion
    activity('deleted')
        ->withProperties($this->backup_data)
        ->log('Deleted ' . $this->entity);
}

Top Menu Level Control

public function index(Request $request)
{
    // Control which level of menu to show in top navigation
    $this->generateTopMenu(1); // Show children of active group (default)
    // $this->generateTopMenu(0); // Show main groups instead
    
    return parent::index($request);
}

Advanced Features

Custom Actions

public function customAction(Request $request, $id)
{
    $item = $this->model->findOrFail($id);
    
    // Perform custom logic
    $result = $this->performCustomOperation($item);
    
    $message = $result ? 'Operation completed successfully' : 'Operation failed';
    $type = $result ? 'success' : 'error';
    
    return redirect()
        ->route($this->controller_index_route)
        ->with($type, $message);
}
public function index(Request $request)
{
    // Handle modal requests
    if ($request->filled('modal') && $request->filled('item_id')) {
        $modalType = $request->input('modal');
        $itemId = $request->input('item_id');
        
        switch ($modalType) {
            case 'show-details':
                $this->extra_props['modalData'] = $this->model
                    ->with(['relations'])
                    ->findOrFail($itemId);
                break;
        }
    }
    
    return parent::index($request);
}

Complete Example

<?php

namespace App\Http\Controllers\Directory;

use App\Http\Controllers\Core\AdminController;
use App\Models\Directory\Member;
use Illuminate\Http\Request;
use Illuminate\Support\Str;

class MemberController extends AdminController
{
    public function __construct()
    {
        // Required properties
        $this->modelClass = Member::class;
        $this->entity = 'Member';
        $this->namespace = 'Directory';
        
        // Must call parent constructor after setting required properties
        parent::__construct();
        
        // Configure controller
        $this->setupController();
    }
    
    private function setupController()
    {
        // Inertia pages
        $this->inertia_page_index = 'Directory/Member/Index';
        $this->inertia_page_create = 'Directory/Member/Create';
        $this->inertia_page_edit = 'Directory/Member/Edit';
        $this->inertia_page_view = 'Directory/Member/View';
        
        // Routes
        $this->controller_base_route = 'directory.member';
        $this->controller_index_route = 'directory.member.index';
        
        // Authorization
        $this->auth_entity = 'all-members';
        
        // Search & sort configuration
        $this->searchable_fields = ['name', 'email', 'phone', 'address'];
        $this->sortable_fields = ['created_at', 'updated_at', 'name', 'email'];
        
        // Validation
        $this->validator_create = [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:members,email',
            'phone' => 'nullable|string|max:20',
        ];
        
        $this->validator_update = [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:members,email,' . $this->request_id,
            'phone' => 'nullable|string|max:20',
        ];
    }
    
    public function additionalQuery(Request $request, $query, ?string $type = null)
    {
        if ($type === 'index') {
            $query->with(['category', 'status']);
        }
        
        return $query;
    }
    
    public function index(Request $request)
    {
        $this->data_object_extra = [
            'totalMembers' => Member::count(),
            'activeMembers' => Member::where('status', 'active')->count(),
        ];
        
        $this->breadcrumbs = [
            ['title' => 'Dashboard', 'href' => route('main.dashboard.index')],
            ['title' => 'Directory', 'href' => route('directory.dashboard.index')],
            ['title' => 'Member List', 'href' => route($this->controller_index_route)],
        ];
        
        return parent::index($request);
    }
}

Best Practices

  1. Always call parent::__construct() after setting required properties
  2. Set breadcrumbs in each page method for better navigation
  3. Use lifecycle hooks for complex business logic
  4. Configure search and sort fields for better user experience
  5. Add proper validation rules for data integrity
  6. Use consistent naming conventions for routes and pages
  7. Handle authorization through the auth_entity property
  8. Add meaningful extra_props for enhanced UI functionality