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
}
menu.json
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
| Type | Convention | Example |
|---|---|---|
| Addon Name | PascalCase | EmailValidator |
| Alias | lowercase | emailvalidator |
| Controllers | PascalCase + Controller | SettingsController |
| Models | Singular PascalCase | ValidationRule |
| Migrations | snake_case with date | 2024_01_01_create_rules_table |
| Views | kebab-case | validation-settings.blade.php |
File Organization Tips
- Group by feature - For large addons, organize by feature rather than type
- Keep controllers thin - Move business logic to services or actions
- Use form requests - Validate input in dedicated request classes
- 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