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.