Getting Started - Custom Module Development
Minimum Version Requirement
This feature requires WhatsMarkSaaS v2.0.0 or higher. Ensure your installation meets this requirement before proceeding.
Prerequisites
You must have working knowledge of the following technologies before proceeding:
- Laravel 12 - Service providers, routing, migrations, Eloquent ORM
- Livewire 3 - Component lifecycle, properties, actions
- Tailwind CSS v3 - Utility-first CSS framework
- Alpine.js v3 - Frontend interactivity
- Multi-tenant architecture - Tenant-aware data operations and isolation patterns
Overview
Custom modules extend WhatsMarkSaaS functionality without modifying core code. Each module is a self-contained package that can include:
- Routes (web and API endpoints)
- Controllers and middleware
- Eloquent models with tenant scoping
- Blade templates and Livewire components
- Translations with multi-tenant support
- Configuration files
- Database migrations and seeders
- Event hooks (WordPress-style actions and filters)
- Frontend assets (JavaScript, CSS compiled via Vite)
Modules reside in the Modules/ directory at the project root and can be activated, deactivated, and removed independently.
Step 1: Create a Custom Module
Execute the following Artisan command to generate module scaffolding:
php artisan module:make OrderTrackerThis command generates a complete module structure at Modules/OrderTracker/.
Command options:
| Option | Description |
|---|---|
--force | Overwrites existing module files |
| (none) | Creates new module (fails if exists) |
Step 2: Review Generated Structure
The scaffolding command generates the following directory structure:
Modules/OrderTracker/
Config/
config.php
Console/
Database/
Migrations/
Seeders/
Factories/
Http/
Controllers/
OrderTrackerController.php
Middleware/
Livewire/
Models/
Providers/
OrderTrackerServiceProvider.php
RouteServiceProvider.php
resources/
assets/
js/
css/
lang/
en.json
tenant_en.json
views/
index.blade.php
Routes/
web.php
api.php
OrderTracker.php
module.json
composer.json
package.json
vite.config.js
README.mdStep 3: Understand the Service Provider
The generated OrderTrackerServiceProvider.php handles module registration:
boot() Method
The boot() method registers module resources:
public function boot(): void
{
// Register module translations
$this->loadTranslationsFrom(module_path($this->moduleName, 'resources/lang'), 'ordertracker');
// Publish module configuration
$this->publishes([
module_path($this->moduleName, 'Config/config.php') => config_path('ordertracker.php'),
], 'ordertracker-config');
// Register module views
$this->loadViewsFrom(__DIR__.'/../resources/views', 'ordertracker');
// Load module migrations
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations'));
}register() Method
The register() method registers the route service provider:
public function register(): void
{
$this->app->register(RouteServiceProvider::class);
}Registering Livewire Components
Add Livewire component registration in the boot() method:
use Livewire\Livewire;
public function boot(): void
{
// ... existing registrations ...
// Register Livewire components
Livewire::component('order-tracker::dashboard', \Modules\OrderTracker\Livewire\Dashboard::class);
}Step 4: Configure the Main Module Class
The OrderTracker.php class extends Corbital\ModuleManager\Support\Module and provides lifecycle methods:
<?php
namespace Modules\OrderTracker;
use Corbital\ModuleManager\Support\Module;
class OrderTracker extends Module
{
/**
* Register WordPress-style action and filter hooks.
*/
public function registerHooks(): void
{
// Register hooks here
}
/**
* Execute operations during module activation.
* Runs before migrations and seeders.
*/
public function activate(): void
{
// Setup tasks
}
/**
* Execute operations during module deactivation.
*/
public function deactivate(): void
{
// Cleanup tasks
}
/**
* Execute operations after activation completes.
* Runs after migrations and seeders.
*/
public function activated(): void
{
// Post-activation tasks
}
/**
* Execute operations after deactivation completes.
*/
public function deactivated(): void
{
// Post-deactivation tasks
}
}Lifecycle Method Execution Order
Activation sequence:
activate()- Pre-activation setup- Run migrations (if
auto_migrationsenabled) - Run seeders (if
auto_migrations.seedenabled) - Register routes, views, translations
activated()- Post-activation tasks
Deactivation sequence:
deactivate()- Pre-deactivation cleanup- Unregister routes
deactivated()- Post-deactivation tasks
Step 5: Activate the Module
Execute the activation command:
php artisan module:activate OrderTrackerWhat happens during activation:
- Migrations run automatically (if
auto_migrationsenabled in config) - Seeders execute after migrations complete
- Routes become available and registered with Laravel
- Views and translations are registered
- The
activated()lifecycle method is called
Configuration for auto-migrations:
Check config/modules.php:
'auto_migrations' => [
'enabled' => true,
'seed' => true,
],Step 6: Access the Module
After activation, the module routes are accessible via browser:
https://your-domain.com/order-trackerThe route prefix is automatically generated as kebab-case from the module name.
Route examples:
| Module Name | Route Prefix |
|---|---|
| OrderTracker | /order-tracker |
| ProductCatalog | /product-catalog |
| CustomerSupport | /customer-support |
Step 7: Deactivate (When Needed)
To disable the module without deleting files:
php artisan module:deactivate OrderTrackerWhat happens during deactivation:
- Routes are unregistered
- The
deactivate()lifecycle method is called - Module files remain on disk
- Database tables and data are preserved
Understanding module.json
The module.json file defines module metadata and dependencies:
{
"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 |
alias | string | URL-friendly identifier (kebab-case) |
namespace | string | PSR-4 namespace root |
provider | string | Main service provider class (FQCN) |
version | string | Semantic version (e.g., 1.0.0) |
order | integer | Load order (lower = earlier, default: 0) |
require | array | Module dependencies (other module names) |
requires_at | string | Minimum WhatsMarkSaaS version required |
conflicts | array | Modules that conflict with this module |
type | string | Always custom for developer modules |
View and Translation Loading
View Namespace Convention
Views are loaded with lowercase module name as namespace:
return view('ordertracker::index', ['data' => $data]);Loading priority (first match wins):
resources/views/modules/ordertracker/- Override location (theme customization)Modules/OrderTracker/resources/views/- Default module views
Translation Files
Standard translations - resources/lang/en.json:
{
"Order Tracker": "Order Tracker",
"View Orders": "View Orders"
}Tenant-specific translations - resources/lang/tenant_en.json:
{
"welcome_message": "Welcome to Order Tracker"
}Usage in views:
{{ t('Order Tracker') }}Loading priority (first match wins):
resources/lang/modules/ordertracker/- Override locationModules/OrderTracker/resources/lang/- Default module translations
Next Steps
You have successfully created and activated a custom module. Proceed with:
- Module Structure Reference - Understand file organization and conventions
- Routing & Controllers - Define web and API routes
- Views & Translations - Create Blade templates and multi-language support
- Hooks & Events - Integrate with WhatsMarkSaaS hook system
- Helper Functions - Use module utilities and helpers
Troubleshooting
Module routes not showing
- Verify activation status:
php artisan module:activate ModuleName - Clear route cache:
php artisan route:clear - Check route list:
php artisan route:list --path=module-name
Views not found
- Verify view files exist in
Modules/ModuleName/resources/views/ - Use correct namespace:
modulename::viewfile(lowercase, no hyphens) - Clear view cache:
php artisan view:clear
Translations not loading
- Check JSON files exist in
Modules/ModuleName/resources/lang/ - Ensure valid JSON format (no trailing commas)
- Clear translation cache:
php artisan cache:clear
Migration errors on activation
- Check migration files for syntax errors
- Verify
tenant_idcolumn is included where needed - Run manually:
php artisan migrate --path=Modules/ModuleName/Database/Migrations
Module not autoloading
- Verify
composer.jsonhas correct namespace:Modules\\ModuleName\\ - Run:
composer dump-autoload - Check
module.jsonhas correctproviderpath
This guide is for WhatsMarkSaaS v2.0.0+ custom module development.