Module Structure Reference
Minimum Version Requirement
This feature requires WhatsMarkSaaS v2.0.0 or higher. Ensure your installation meets this requirement before proceeding.
Overview
This reference documents the complete directory structure and file organization for WhatsMarkSaaS custom modules. Understanding this structure is required for creating, maintaining, and extending modules effectively.
The modular structure ensures clean separation of concerns, making it possible to develop, test, deploy, and maintain integrations independently of the core application.
Module Architecture Diagram
Module Creation Command
Execute the following command to generate the complete module structure:
php artisan module:make OrderTrackerCommand options:
| Option | Description |
|---|---|
| (none) | Creates new module (fails if exists) |
--force | Overwrites existing module files |
Expected output:
[OrderTracker] module created successfully.
Module type: Custom
To activate the module, run: php artisan module:activate OrderTrackerComplete Directory Structure
The module generation command creates the following structure:
Modules/OrderTracker/
composer.json # Module-specific Composer configuration
module.json # Module metadata and configuration
package.json # Frontend dependencies and build scripts
README.md # Module documentation
OrderTracker.php # Main module class
vite.config.js # Vite configuration for asset compilation
Config/
config.php # Module configuration settings
Console/ # Artisan commands directory
Database/
Factories/ # Model factories for testing
Migrations/ # Database schema migrations
Seeders/ # Database seeders
Http/
Controllers/
OrderTrackerController.php # Main controller
Middleware/ # Custom middleware
Livewire/ # Livewire components
Models/ # Eloquent models
Providers/
RouteServiceProvider.php # Routes service provider
OrderTrackerServiceProvider.php # Main service provider
resources/
assets/
css/
app.css # Module-specific CSS
js/
app.js # Module-specific JavaScript
lang/
en.json # Standard translations
tenant_en.json # Tenant-specific translations
views/
index.blade.php # Default view template
Routes/
api.php # API routes
web.php # Web routesRoot Level Files
module.json
The module.json file defines module metadata, dependencies, and configuration.
Required structure:
{
"name": "OrderTracker",
"alias": "order-tracker",
"namespace": "Modules\\OrderTracker\\",
"provider": "Modules\\OrderTracker\\Providers\\OrderTrackerServiceProvider",
"author": "Module Author",
"url": "",
"version": "1.0.0",
"description": "The OrderTracker Module",
"keywords": [],
"order": 0,
"providers": [
"Modules\\OrderTracker\\Providers\\OrderTrackerServiceProvider"
],
"aliases": [],
"require": [],
"requires_at": "2.0.0",
"conflicts": [],
"type": "custom"
}Field reference:
| Field | Type | Description |
|---|---|---|
name | string | Module name in PascalCase (e.g., OrderTracker) |
alias | string | URL-friendly identifier in kebab-case (e.g., order-tracker) |
namespace | string | PSR-4 namespace root with trailing backslash |
provider | string | Fully qualified class name of main service provider |
author | string | Module author name |
url | string | Module homepage or repository URL |
version | string | Semantic version (major.minor.patch) |
description | string | Brief module description |
order | integer | Load order priority (lower = earlier, default: 0) |
providers | array | List of service provider class names |
require | array | Module dependencies (other module names) |
requires_at | string | Minimum WhatsMarkSaaS version required |
conflicts | array | Incompatible module names |
type | string | Module type (always "custom" for developer modules) |
composer.json
The composer.json file defines PSR-4 autoloading for the module namespace.
Required structure:
{
"name": "modules/order-tracker",
"description": "Order Tracker Module",
"authors": [
{
"name": "Module Author",
"email": "[email protected]"
}
],
"autoload": {
"psr-4": {
"Modules\\OrderTracker\\": ""
}
}
}Key requirements:
- Namespace must match
module.json - PSR-4 autoloading must map to module root
- Run
composer dump-autoloadafter creating module
OrderTracker.php (Main Module Class)
The main module class extends Corbital\ModuleManager\Support\Module:
<?php
namespace Modules\OrderTracker;
use Corbital\ModuleManager\Support\Module;
class OrderTracker extends Module
{
/**
* Register WordPress-style action and filter hooks.
*
* @return void
*/
public function registerHooks(): void
{
// Register hooks
}
/**
* Execute operations during module activation.
* Runs before migrations and seeders.
*
* @return void
*/
public function activate(): void
{
// Pre-activation setup
}
/**
* Execute operations during module deactivation.
*
* @return void
*/
public function deactivate(): void
{
// Pre-deactivation cleanup
}
/**
* Execute operations after activation completes.
* Runs after migrations and seeders.
*
* @return void
*/
public function activated(): void
{
// Post-activation tasks
}
/**
* Execute operations after deactivation completes.
*
* @return void
*/
public function deactivated(): void
{
// Post-deactivation tasks
}
}package.json
Frontend dependencies and build script configuration:
{
"name": "order-tracker",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"vite": "^5.0.0",
"@vitejs/plugin-vue": "^5.0.0"
}
}vite.config.js
Vite configuration for asset compilation:
import { defineConfig } from 'vite';
import { resolve } from 'path';
export default defineConfig({
build: {
outDir: resolve(__dirname, 'public/build'),
emptyOutDir: true,
manifest: 'manifest.json',
rollupOptions: {
input: {
'app': resolve(__dirname, 'resources/assets/js/app.js'),
'styles': resolve(__dirname, 'resources/assets/css/app.css'),
}
}
}
});Directory Reference
Config/
Module-specific configuration files.
Purpose: Centralized module settings accessible via module_config() helper.
Example (Config/config.php):
<?php
return [
'name' => 'OrderTracker',
'api_version' => 'v1',
'timeout' => 30,
'defaults' => [
'status' => 'pending',
'priority' => 'normal',
],
];Access configuration:
$timeout = module_config('OrderTracker', 'timeout', 30);Console/
Artisan console commands.
Purpose: Custom CLI commands for module operations.
Example structure:
Console/
Commands/
ProcessOrdersCommand.phpExample command:
<?php
namespace Modules\OrderTracker\Console\Commands;
use Illuminate\Console\Command;
class ProcessOrdersCommand extends Command
{
protected $signature = 'order-tracker:process';
protected $description = 'Process pending orders';
public function handle(): void
{
$this->info('Processing orders...');
}
}Register in service provider:
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->commands([
\Modules\OrderTracker\Console\Commands\ProcessOrdersCommand::class,
]);
}
}Database/
Database-related files (migrations, seeders, factories).
Database/Migrations/
Schema migrations for module tables.
Naming convention:
YYYY_MM_DD_HHMMSS_create_table_name_table.phpExample migration:
<?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('orders')) {
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('tenant_id')->index();
$table->string('order_number')->unique();
$table->string('status')->default('pending');
$table->decimal('total', 10, 2);
$table->timestamps();
$table->foreign('tenant_id')
->references('id')
->on('tenants')
->cascadeOnDelete();
});
}
}
public function down(): void
{
Schema::dropIfExists('orders');
}
};Critical requirements:
- Always include
tenant_idcolumn with foreign key constraint - Use
if (! Schema::hasTable())to prevent duplicate table errors - Index
tenant_idfor performance
Database/Seeders/
Database seeders for initial data.
Example seeder:
<?php
namespace Modules\OrderTracker\Database\Seeders;
use Illuminate\Database\Seeder;
class OrderTrackerDatabaseSeeder extends Seeder
{
public function run(): void
{
// Seed initial data
}
}Database/Factories/
Model factories for testing.
Example factory:
<?php
namespace Modules\OrderTracker\Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Modules\OrderTracker\Models\Order;
class OrderFactory extends Factory
{
protected $model = Order::class;
public function definition(): array
{
return [
'tenant_id' => 1,
'order_number' => $this->faker->unique()->uuid(),
'status' => 'pending',
'total' => $this->faker->randomFloat(2, 10, 1000),
];
}
}Http/
HTTP-related files (controllers, middleware).
Http/Controllers/
Request handling controllers.
Example controller:
<?php
namespace Modules\OrderTracker\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
class OrderTrackerController extends Controller
{
public function index()
{
return view('ordertracker::index');
}
}Http/Middleware/
Custom middleware.
Example middleware:
<?php
namespace Modules\OrderTracker\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class CheckOrderAccess
{
public function handle(Request $request, Closure $next)
{
// Authorization logic
return $next($request);
}
}Livewire/
Livewire component classes.
Example component:
<?php
namespace Modules\OrderTracker\Livewire;
use Livewire\Component;
class OrderList extends Component
{
public function render()
{
return view('ordertracker::livewire.order-list');
}
}Register in service provider:
use Livewire\Livewire;
public function boot(): void
{
Livewire::component('order-tracker::order-list', \Modules\OrderTracker\Livewire\OrderList::class);
}Models/
Eloquent models.
Example model with tenant scoping:
<?php
namespace Modules\OrderTracker\Models;
use Illuminate\Database\Eloquent\Model;
use App\Traits\BelongsToTenant;
class Order extends Model
{
use BelongsToTenant;
protected $fillable = [
'tenant_id',
'order_number',
'status',
'total',
];
protected $casts = [
'total' => 'decimal:2',
];
}Critical requirements:
- Use
BelongsToTenanttrait for tenant-scoped models - Include
tenant_idin$fillablearray
Providers/
Service providers for module registration.
Providers/OrderTrackerServiceProvider.php
Main service provider registers module resources:
<?php
namespace Modules\OrderTracker\Providers;
use Illuminate\Support\ServiceProvider;
class OrderTrackerServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Register translations
$this->loadTranslationsFrom(
module_path('OrderTracker', 'resources/lang'),
'ordertracker'
);
// Publish configuration
$this->publishes([
module_path('OrderTracker', 'Config/config.php') => config_path('ordertracker.php'),
], 'ordertracker-config');
// Register views
$this->loadViewsFrom(__DIR__.'/../resources/views', 'ordertracker');
// Load migrations
$this->loadMigrationsFrom(module_path('OrderTracker', 'Database/Migrations'));
}
public function register(): void
{
$this->app->register(RouteServiceProvider::class);
}
}Providers/RouteServiceProvider.php
Route registration service provider:
<?php
namespace Modules\OrderTracker\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
public function boot(): void
{
parent::boot();
$this->map();
}
public function map(): void
{
$this->mapWebRoutes();
$this->mapApiRoutes();
}
protected function mapWebRoutes(): void
{
Route::middleware('web')
->group(module_path('OrderTracker', 'Routes/web.php'));
}
protected function mapApiRoutes(): void
{
Route::middleware('api')
->prefix('api')
->group(module_path('OrderTracker', 'Routes/api.php'));
}
}resources/
Frontend assets, translations, and views.
resources/assets/
JavaScript and CSS files.
Structure:
resources/assets/
css/
app.css
js/
app.jsresources/lang/
Translation files.
Standard translations (en.json):
{
"Order Tracker": "Order Tracker",
"View Orders": "View Orders",
"Create Order": "Create Order"
}Tenant-specific translations (tenant_en.json):
{
"welcome_message": "Welcome to Order Tracker"
}resources/views/
Blade templates.
Example view:
<x-app-layout>
<x-slot name="title">Order Tracker</x-slot>
<h1 class="text-2xl font-bold">{{ t('Order Tracker') }}</h1>
</x-app-layout>Routes/
Route definition files.
Routes/web.php
Web routes:
<?php
use Illuminate\Support\Facades\Route;
use Modules\OrderTracker\Http\Controllers\OrderTrackerController;
Route::middleware('web')->group(function () {
Route::prefix('order-tracker')->group(function () {
Route::get('/', [OrderTrackerController::class, 'index'])
->name('order-tracker.index');
});
});Routes/api.php
API routes:
<?php
use Illuminate\Support\Facades\Route;
Route::middleware('api')->prefix('api')->group(function () {
Route::prefix('order-tracker')->group(function () {
// API routes
});
});Namespace Conventions
All module classes must follow PSR-4 autoloading standards:
| Directory | Namespace |
|---|---|
| Http/Controllers/ | Modules\OrderTracker\Http\Controllers |
| Models/ | Modules\OrderTracker\Models |
| Livewire/ | Modules\OrderTracker\Livewire |
| Providers/ | Modules\OrderTracker\Providers |
| Console/Commands/ | Modules\OrderTracker\Console\Commands |
Next Steps
- Routing & Controllers - Define web and API routes
- Views & Translations - Create Blade templates and translations
- Hooks & Events - Integrate with hook system
- Helper Functions - Use module utilities
This reference is for WhatsMarkSaaS v2.0.0+ custom module development.