Skip to main content

Creating an Addon

This guide walks you through creating a new addon from scratch using the Artisan command-line tool.

Generate the Addon

Open your terminal, navigate to the Mumara Campaigns root directory, and run:

php artisan module:make YourAddonName

Replace YourAddonName with your addon's name using PascalCase (e.g., EmailValidator, CrmConnector, AdvancedReports).

Create Multiple Addons

You can create multiple addons at once:

php artisan module:make EmailValidator CrmConnector AdvancedReports

Generated Structure

The command creates a new directory under /Addons/YourAddonName/ with the following structure:

Addons/YourAddonName/
├── module.json
├── composer.json
├── package.json
├── webpack.mix.js
├── functions.php
├── menu.json
├── Config/
│ └── config.php
├── Console/
├── Database/
│ ├── Migrations/
│ └── Seeders/
│ └── YourAddonNameDatabaseSeeder.php
├── Entities/
├── Http/
│ ├── Controllers/
│ │ └── YourAddonNameController.php
│ ├── Middleware/
│ └── Requests/
├── Providers/
│ ├── YourAddonNameServiceProvider.php
│ ├── EventServiceProvider.php
│ └── RouteServiceProvider.php
├── Resources/
│ ├── assets/
│ │ ├── js/
│ │ │ └── app.js
│ │ └── sass/
│ │ └── app.scss
│ ├── lang/
│ └── views/
│ ├── layouts/
│ │ └── master.blade.php
│ └── index.blade.php
├── Routes/
│ ├── web.php
│ └── api.php
├── Settings/
│ ├── config.php
│ ├── install/
│ └── uninstall/
├── hooks/
│ └── hooks.php
└── Tests/
├── Feature/
└── Unit/

Configure the Addon

After generation, configure your addon's metadata.

1. Edit module.json

The module.json file is your addon's manifest:

{
"name": "YourAddonName",
"alias": "youraddonname",
"description": "Description of what your addon does",
"keywords": ["mumara", "addon", "feature"],
"priority": 0,
"providers": [
"Addons\\YourAddonName\\Providers\\YourAddonNameServiceProvider",
"Addons\\YourAddonName\\Providers\\EventServiceProvider"
],
"files": [],
"requires": []
}
FieldDescription
nameAddon name (PascalCase)
aliasLowercase identifier used in views and routes
descriptionBrief description of the addon
keywordsSearch keywords
priorityLoad order (lower loads first)
providersService providers to register
filesAdditional PHP files to autoload
requiresDependencies on other addons

2. Edit Settings/config.php

Configure addon properties displayed in the admin panel:

<?php

$setting = [
'name' => 'Your Addon Name',
'vendor' => 'Your Company',
'type' => 'Integration',
'logo' => 'logo.png',
'version' => '1.0.0',
'route' => 'youraddon',
'install_dir' => 'YourAddonName',
'license_type' => 'free', // native, marketplace, remote, or free
'new_version_url' => '',
'update_url' => '',
'license_key' => ''
];
FieldDescription
nameDisplay name in addon manager
vendorYour company/developer name
typeCategory (Integration, Feature, Utility, etc.)
logoLogo filename in Settings/ directory
versionCurrent version number
routeBase route prefix for your addon
install_dirDirectory name under /Addons
license_typeLicensing model
new_version_urlURL to check for updates
update_urlURL to download updates
license_keyLicense key (if applicable)

3. Configure composer.json

Set up PHP autoloading and dependencies:

{
"name": "yourcompany/youraddonname",
"description": "Your addon description",
"type": "library",
"require": {},
"autoload": {
"psr-4": {
"Addons\\YourAddonName\\": ""
}
}
}

Set Up the Service Provider

The main service provider bootstraps your addon. Edit Providers/YourAddonNameServiceProvider.php:

<?php

namespace Addons\YourAddonName\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Console\Scheduling\Schedule;

class YourAddonNameServiceProvider extends ServiceProvider
{
protected string $moduleName = 'YourAddonName';
protected string $moduleNameLower = 'youraddonname';

public function boot(): void
{
$this->registerHooks();
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/Migrations'));
}

public function register(): void
{
$this->app->register(RouteServiceProvider::class);
$this->app->register(EventServiceProvider::class);
}

protected function registerHooks(): void
{
foreach (glob(module_path($this->moduleName, 'hooks/*.php')) as $file) {
require_once $file;
}
}

protected function registerTranslations(): void
{
$langPath = resource_path('lang/modules/' . $this->moduleNameLower);

if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
} else {
$this->loadTranslationsFrom(module_path($this->moduleName, 'Resources/lang'), $this->moduleNameLower);
}
}

protected function registerConfig(): void
{
$this->publishes([
module_path($this->moduleName, 'Config/config.php') => config_path($this->moduleNameLower . '.php'),
], 'config');

$this->mergeConfigFrom(
module_path($this->moduleName, 'Config/config.php'),
$this->moduleNameLower
);
}

protected function registerViews(): void
{
$viewPath = resource_path('views/modules/' . $this->moduleNameLower);
$sourcePath = module_path($this->moduleName, 'Resources/views');

$this->publishes([
$sourcePath => $viewPath
], ['views', $this->moduleNameLower . '-views']);

$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
}

private function getPublishableViewPaths(): array
{
$paths = [];
foreach (config('view.paths') as $path) {
if (is_dir($path . '/modules/' . $this->moduleNameLower)) {
$paths[] = $path . '/modules/' . $this->moduleNameLower;
}
}
return $paths;
}
}

Create Your First Route

Edit Routes/web.php to add routes:

<?php

use Illuminate\Support\Facades\Route;
use Addons\YourAddonName\Http\Controllers\YourAddonNameController;

Route::middleware(['web', 'auth'])->prefix('youraddon')->group(function () {
Route::get('/', [YourAddonNameController::class, 'index'])->name('youraddon.index');
Route::get('/settings', [YourAddonNameController::class, 'settings'])->name('youraddon.settings');
});

Create a Controller

Edit Http/Controllers/YourAddonNameController.php:

<?php

namespace Addons\YourAddonName\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;

class YourAddonNameController extends Controller
{
public function __construct()
{
$this->middleware(['auth', '2fa']);
}

public function index()
{
return view('youraddonname::index', [
'title' => 'Your Addon'
]);
}

public function settings()
{
return view('youraddonname::settings');
}
}

Create a View

Edit Resources/views/index.blade.php:

@extends('layouts.master2')

@section('title', $title)

@section('content')
<div class="kt-container kt-container--fluid kt-grid__item kt-grid__item--fluid">
<div class="kt-portlet">
<div class="kt-portlet__head">
<div class="kt-portlet__head-label">
<h3 class="kt-portlet__head-title">{{ $title }}</h3>
</div>
</div>
<div class="kt-portlet__body">
<p>Welcome to your addon!</p>
</div>
</div>
</div>
@endsection

Add a Menu Item

Edit menu.json to add navigation:

[
{
"title": "Your Addon",
"icon": "<i class='kt-menu__link-icon flaticon-app'></i>",
"url": "youraddon",
"hidden": false,
"hasChild": false,
"childrens": []
}
]

For nested menus:

[
{
"title": "Your Addon",
"icon": "<i class='kt-menu__link-icon flaticon-app'></i>",
"url": "#",
"hidden": false,
"hasChild": true,
"childrens": [
{
"title": "Dashboard",
"icon": "<i class='menu-bullet menu-bullet-dot'><span></span></i>",
"url": "youraddon",
"hidden": false
},
{
"title": "Settings",
"icon": "<i class='menu-bullet menu-bullet-dot'><span></span></i>",
"url": "youraddon/settings",
"hidden": false
}
]
}
]

Install the Addon

After setting up your addon:

  1. Log into Mumara Campaigns as admin
  2. Navigate to Setup > Addons
  3. Find your addon in the list
  4. Click Install

The system will:

  • Run database migrations
  • Publish translations and configuration
  • Enable the addon

Verify Installation

After installation:

  1. Clear the cache: php artisan cache:clear
  2. Navigate to your addon's route (e.g., /youraddon)
  3. Verify the menu item appears in the sidebar

Next Steps