Skip to content

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:

bash
php artisan module:make OrderTracker

Command options:

OptionDescription
(none)Creates new module (fails if exists)
--forceOverwrites existing module files

Expected output:

text
[OrderTracker] module created successfully.
Module type: Custom
To activate the module, run: php artisan module:activate OrderTracker

Complete 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 routes

Root Level Files

module.json

The module.json file defines module metadata, dependencies, and configuration.

Required structure:

json
{
  "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:

FieldTypeDescription
namestringModule name in PascalCase (e.g., OrderTracker)
aliasstringURL-friendly identifier in kebab-case (e.g., order-tracker)
namespacestringPSR-4 namespace root with trailing backslash
providerstringFully qualified class name of main service provider
authorstringModule author name
urlstringModule homepage or repository URL
versionstringSemantic version (major.minor.patch)
descriptionstringBrief module description
orderintegerLoad order priority (lower = earlier, default: 0)
providersarrayList of service provider class names
requirearrayModule dependencies (other module names)
requires_atstringMinimum WhatsMarkSaaS version required
conflictsarrayIncompatible module names
typestringModule type (always "custom" for developer modules)

composer.json

The composer.json file defines PSR-4 autoloading for the module namespace.

Required structure:

json
{
  "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-autoload after creating module

OrderTracker.php (Main Module Class)

The main module class extends Corbital\ModuleManager\Support\Module:

php
<?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:

json
{
  "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:

javascript
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
<?php

return [
    'name' => 'OrderTracker',
    'api_version' => 'v1',
    'timeout' => 30,
    'defaults' => [
        'status' => 'pending',
        'priority' => 'normal',
    ],
];

Access configuration:

php
$timeout = module_config('OrderTracker', 'timeout', 30);

Console/

Artisan console commands.

Purpose: Custom CLI commands for module operations.

Example structure:

Console/
 Commands/
     ProcessOrdersCommand.php

Example command:

php
<?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:

php
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.php

Example migration:

php
<?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_id column with foreign key constraint
  • Use if (! Schema::hasTable()) to prevent duplicate table errors
  • Index tenant_id for performance

Database/Seeders/

Database seeders for initial data.

Example seeder:

php
<?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
<?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
<?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
<?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
<?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:

php
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
<?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 BelongsToTenant trait for tenant-scoped models
  • Include tenant_id in $fillable array

Providers/

Service providers for module registration.

Providers/OrderTrackerServiceProvider.php

Main service provider registers module resources:

php
<?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
<?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.js

resources/lang/

Translation files.

Standard translations (en.json):

json
{
    "Order Tracker": "Order Tracker",
    "View Orders": "View Orders",
    "Create Order": "Create Order"
}

Tenant-specific translations (tenant_en.json):

json
{
    "welcome_message": "Welcome to Order Tracker"
}

resources/views/

Blade templates.

Example view:

blade
<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
<?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
<?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:

DirectoryNamespace
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


This reference is for WhatsMarkSaaS v2.0.0+ custom module development.

© 2024 - Corbital Technologies. All rights reserved.