Skip to main content

Directory Structure

Every addon follows a standardized directory structure that mirrors Laravel's application layout. Understanding this structure is essential for organizing your code effectively.

Complete Structure

Addons/YourAddonName/
├── module.json # Addon manifest (required)
├── composer.json # PHP dependencies
├── package.json # NPM dependencies
├── webpack.mix.js # Asset compilation
├── functions.php # Lifecycle functions
├── menu.json # Navigation menu

├── Config/ # Configuration files
│ └── config.php

├── Console/ # Artisan commands
│ └── Commands/
│ └── YourCommand.php

├── Database/
│ ├── Migrations/ # Database migrations
│ │ └── 2024_01_01_create_table.php
│ └── Seeders/ # Data seeders
│ └── YourAddonSeeder.php

├── Entities/ # Eloquent models
│ └── YourModel.php

├── Helpers/ # Helper functions
│ └── helper.php

├── hooks/ # Hook implementations
│ └── hooks.php

├── Http/
│ ├── Controllers/ # Request handlers
│ │ └── YourController.php
│ ├── Middleware/ # HTTP middleware
│ │ └── YourMiddleware.php
│ └── Requests/ # Form validation
│ └── YourFormRequest.php

├── Jobs/ # Queue jobs
│ └── YourJob.php

├── Listeners/ # Event listeners
│ └── YourListener.php

├── Providers/ # Service providers
│ ├── YourAddonServiceProvider.php
│ ├── EventServiceProvider.php
│ └── RouteServiceProvider.php

├── Resources/
│ ├── assets/ # Frontend assets
│ │ ├── js/
│ │ │ └── app.js
│ │ ├── sass/
│ │ │ └── app.scss
│ │ └── css/
│ ├── lang/ # Translations
│ │ └── en/
│ │ └── app.php
│ └── views/ # Blade templates
│ ├── layouts/
│ └── index.blade.php

├── Routes/ # Route definitions
│ ├── web.php # Web routes
│ └── api.php # API routes

├── Rules/ # Validation rules
│ └── CustomRule.php

├── Settings/ # Addon settings
│ ├── config.php # Addon metadata
│ ├── install/ # Installation SQL
│ │ └── v1.0.sql
│ └── uninstall/ # Uninstall SQL
│ └── uninstall.sql

└── Tests/ # Test files
├── Feature/
└── Unit/

Core Files

module.json

The addon manifest file. This is required for the addon to be recognized.

{
"name": "YourAddonName",
"alias": "youraddonname",
"description": "What your addon does",
"keywords": [],
"priority": 0,
"providers": [
"Addons\\YourAddonName\\Providers\\YourAddonNameServiceProvider"
],
"files": [
"Helpers/helper.php"
],
"requires": []
}

composer.json

Defines PHP dependencies and PSR-4 autoloading.

{
"name": "vendor/youraddonname",
"autoload": {
"psr-4": {
"Addons\\YourAddonName\\": ""
}
},
"require": {
"guzzlehttp/guzzle": "^7.0"
}
}

functions.php

Contains lifecycle functions called during install/update/uninstall:

<?php

function install_addon($addon_name)
{
// Run during addon installation
// Execute SQL files, setup initial data
}

function update_addon($addon_name)
{
// Run during addon update
// Apply incremental changes
}

function uninstall_addon($addon_name)
{
// Run during addon uninstall
// Cleanup data if needed
}

Defines sidebar navigation for your addon. See Configuration for details.

Directory Details

Config/

Configuration files that can be published to the main config/ directory.

// Config/config.php
return [
'name' => 'Your Addon',
'api_endpoint' => env('YOUR_ADDON_API_URL', 'https://api.example.com'),
'cache_ttl' => 3600,
];

Access in code:

config('youraddonname.api_endpoint');

Console/

Artisan commands for CLI operations and scheduled tasks.

// Console/YourCommand.php
namespace Addons\YourAddonName\Console;

use Illuminate\Console\Command;

class YourCommand extends Command
{
protected $signature = 'youraddon:sync';
protected $description = 'Sync data with external service';

public function handle()
{
$this->info('Syncing...');
// Your logic here
}
}

Register in your service provider:

$this->commands([
\Addons\YourAddonName\Console\YourCommand::class,
]);

Database/Migrations/

Database schema changes. Migrations run automatically during installation.

// Database/Migrations/2024_01_01_000000_create_your_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
if (!Schema::hasTable('your_addon_table')) {
Schema::create('your_addon_table', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('name');
$table->timestamps();
});
}
}

public function down(): void
{
Schema::dropIfExists('your_addon_table');
}
};

Entities/

Eloquent models for your addon's database tables.

// Entities/YourModel.php
namespace Addons\YourAddonName\Entities;

use Illuminate\Database\Eloquent\Model;

class YourModel extends Model
{
protected $table = 'your_addon_table';

protected $fillable = ['user_id', 'name'];

public function user()
{
return $this->belongsTo(\App\Models\User::class);
}
}

Helpers/

Global helper functions. Add to module.json files array to autoload:

// Helpers/helper.php
if (!function_exists('your_addon_helper')) {
function your_addon_helper($value)
{
return strtoupper($value);
}
}

hooks/

Hook implementations for extending core functionality. See Hooks Integration.

// hooks/hooks.php
add_hook('AddContact', 5, function ($vars) {
// React to contact addition
$contact = $vars['contact'];
// Your logic
});

Http/Controllers/

Handle HTTP requests and return responses.

// Http/Controllers/YourController.php
namespace Addons\YourAddonName\Http\Controllers;

use App\Http\Controllers\Controller;

class YourController extends Controller
{
public function index()
{
return view('youraddonname::index');
}
}

Http/Middleware/

Custom middleware for request filtering.

// Http/Middleware/CheckSubscription.php
namespace Addons\YourAddonName\Http\Middleware;

use Closure;

class CheckSubscription
{
public function handle($request, Closure $next)
{
if (!auth()->user()->hasActiveSubscription()) {
return redirect()->route('youraddon.subscribe');
}
return $next($request);
}
}

Http/Requests/

Form request validation classes.

// Http/Requests/StoreSettingRequest.php
namespace Addons\YourAddonName\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreSettingRequest extends FormRequest
{
public function rules(): array
{
return [
'api_key' => 'required|string|min:20',
'webhook_url' => 'nullable|url',
];
}
}

Providers/

Service providers that bootstrap your addon.

  • YourAddonServiceProvider.php - Main provider, loads hooks, views, config
  • RouteServiceProvider.php - Loads route files
  • EventServiceProvider.php - Registers event listeners

Resources/assets/

Frontend assets compiled with Laravel Mix.

assets/
├── js/
│ └── app.js # JavaScript entry point
├── sass/
│ └── app.scss # SASS entry point
└── css/
└── custom.css # Plain CSS files

Resources/lang/

Translation files for internationalization.

// Resources/lang/en/app.php
return [
'title' => 'Your Addon',
'messages' => [
'success' => 'Operation completed successfully',
'error' => 'An error occurred',
],
];

Use in views:

{{ trans('youraddonname::app.title') }}
{{ trans('youraddonname::app.messages.success') }}

Resources/views/

Blade templates for your addon's UI.

views/
├── layouts/
│ └── master.blade.php # Optional: addon-specific layout
├── index.blade.php # Main page
├── settings.blade.php # Settings page
└── partials/
└── sidebar.blade.php # Reusable components

Routes/

Route definitions. See Routes & Controllers for details.

// Routes/web.php - Web routes with sessions
// Routes/api.php - Stateless API routes

Settings/

Addon metadata and SQL scripts.

Settings/
├── config.php # Addon metadata for admin panel
├── install/
│ ├── v1.0.sql # Initial install SQL
│ └── v1.1.sql # Version 1.1 migrations
└── uninstall/
└── uninstall.sql # Cleanup SQL

Best Practices

Naming Conventions

TypeConventionExample
Addon NamePascalCaseEmailValidator
Aliaslowercaseemailvalidator
ControllersPascalCase + ControllerSettingsController
ModelsSingular PascalCaseValidationRule
Migrationssnake_case with date2024_01_01_create_rules_table
Viewskebab-casevalidation-settings.blade.php

File Organization Tips

  1. Group by feature - For large addons, organize by feature rather than type
  2. Keep controllers thin - Move business logic to services or actions
  3. Use form requests - Validate input in dedicated request classes
  4. Namespace properly - All classes under Addons\YourAddonName\

Avoid These Patterns

  • Don't put business logic in controllers
  • Don't create helpers that could be services
  • Don't duplicate functionality that exists in the core
  • Don't hardcode configuration values