A complete guide to building, registering, packaging, and distributing extensions for our ecosystem.
Each plugin contains its own routes, views, migrations, permissions, settings, menu placements, and custom logic. Easily extend your SaaS.
TimeTracker/ ├── Controllers/ │ ├── TimeTrackerController.php │ ├── DashboardController.php │ └── ScreenShotController.php ├── Models/ │ ├── TimeTrack.php │ ├── Screenshot.php │ └── TimeTrackerConfig.php ├── Database/ │ └── Migrations/ ├── Providers/ │ └── TimeTrackerServiceProvider.php ├── routes/ │ ├── web.php │ └── api.php ├── Resources/ │ ├── views/ │ └── lang/ │ └── plugin_labels.php ├── public/ │ ├── js/ │ └── css/ ├── menus.php └── plugin.json
Defines metadata like name, version, author, and permissions. Required fields: name, slug, description, version, enabled, provider.
{
"name": "Taskify - Time Tracker",
"slug": "time-tracker",
"description": "Tracks user time and screenshots for productivity analysis.",
"version": "1.0.0",
"enabled": true,
"provider": "Plugins\\TimeTracker\\Providers\\TimeTrackerServiceProvider",
"publish_tag": "timetracker-assets"
}
Organized directory structure with Controllers, Models, Views, Routes, Migrations, and Assets.
TimeTracker/ ├── Controllers/ ├── Models/ ├── Database/ │ └── Migrations/ ├── Providers/ │ └── TimeTrackerServiceProvider.php ├── routes/ │ ├── web.php │ └── api.php ├── Resources/ │ ├── views/ │ └── lang/ ├── public/ │ ├── js/ │ └── css/ ├── menus.php └── plugin.json
Plugins register menu items, routes, and system hooks through the Service Provider's boot() method.
$this->loadRoutesFrom(__DIR__ . '/../routes/web.php'); $this->loadViewsFrom(__DIR__ . '/../Resources/views', 'timetracker'); $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations'); $this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'timetracker'); $this->publishes([...], ['timetracker-assets', 'public']);
Create a new folder in the plugins/ directory with your plugin name in PascalCase (e.g., MyPlugin).
Create a plugin.json file with required metadata: name, slug, description, version, enabled, and provider class.
Create your Service Provider and register routes, views, migrations, and menus in the boot() method.
Set "enabled": true in your plugin.json file. The system will automatically discover and load your plugin.
Run php artisan migrate to apply database migrations, then test your plugin functionality.
namespace Plugins\TimeTracker\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Log;
class TimeTrackerServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
$this->loadRoutesFrom(__DIR__ . '/../routes/api.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'timetracker');
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
$this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'timetracker');
$this->publishes([
__DIR__ . '/../public/js' => public_path('assets/js/timetracker-plugin'),
], ['timetracker-assets', 'public']);
// Log plugin version on load
if (file_exists(__DIR__ . '/../plugin.json')) {
$pluginJson = json_decode(file_get_contents(__DIR__ . '/../plugin.json'), true);
Log::info('✅ TimeTracker Plugin Loaded - Version: ' . ($pluginJson['version'] ?? 'unknown'));
}
}
}
return [
[
'id' => 'team_monitoring_and_productivity_tracker',
'label' => get_label('team_insights', 'Team Insights'),
'url' => route('timetracker.index'),
'icon' => 'bx bx-alarm',
'class' => 'menu-item' . (request()->is('timetracker*') ? ' active open' : ''),
'category' => 'team_monitoring_and_productivity_tracker',
'show' => 1,
'submenus' => [
[
'id' => 'productivity_dashboard',
'label' => get_label('productivity_dashboard', 'Productivity Dashboard'),
'url' => route('timetracker.index'),
'show' => isAdminOrHasAllDataAccess() ? 1 : 0,
],
],
],
];
use Illuminate\Support\Facades\Route;
use Plugins\TimeTracker\Controllers\TimeTrackerController;
Route::middleware(['web', 'auth'])->prefix('timetracker')->group(function () {
Route::get('/', [TimeTrackerController::class, 'index'])->name('timetracker.index');
Route::post('/track', [TimeTrackerController::class, 'storeTime']);
Route::get('/configuration', [TimeTrackerController::class, 'configuration'])
->name('timetracker.configuration');
});
A comprehensive guide covering everything you need to build, test, and deploy Taskify plugins
Create a new folder in the plugins/ directory. Use PascalCase for the folder name (e.g., MyPlugin).
mkdir -p plugins/MyPlugin
Create a plugin.json file in your plugin root with the required metadata.
{
"name": "Taskify - My Plugin",
"slug": "my-plugin",
"description": "Description of your plugin",
"version": "1.0.0",
"enabled": true,
"provider": "Plugins\\MyPlugin\\Providers\\MyPluginServiceProvider"
}
Create your Service Provider in Providers/MyPluginServiceProvider.php.
namespace Plugins\MyPlugin\Providers;
use Illuminate\Support\ServiceProvider;
class MyPluginServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'my-plugin');
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
}
}
Set "enabled": true in plugin.json, then run migrations and test your plugin.
php artisan migrate php artisan cache:clear
Follow this standard directory structure for maintainable plugins. All plugins should be placed in the plugins/ directory.
MyPlugin/ ├── Controllers/ # Plugin controllers │ └── MyPluginController.php ├── Models/ # Eloquent models │ └── MyModel.php ├── Database/ │ └── Migrations/ # Database migrations │ └── YYYY_MM_DD_HHMMSS_create_table.php ├── Providers/ │ └── MyPluginServiceProvider.php ├── routes/ │ ├── web.php # Web routes │ └── api.php # API routes (optional) ├── Resources/ │ ├── views/ # Blade views │ │ └── my-plugin/ │ │ └── index.blade.php │ └── lang/ │ └── plugin_labels.php ├── public/ │ ├── js/ # JavaScript files │ ├── css/ # CSS files │ └── img/ # Images ├── Services/ # Business logic (optional) ├── Commands/ # Artisan commands (optional) ├── Middleware/ # Custom middleware (optional) ├── menus.php # Menu configuration ├── plugin.json # Plugin manifest (REQUIRED) └── README.md # Plugin documentation
SocialMediaManagement)The Service Provider is the heart of your plugin. It registers routes, views, migrations, translations, assets, commands, and services.
namespace Plugins\TimeTracker\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Log;
class TimeTrackerServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Load routes
$this->loadRoutesFrom(__DIR__ . '/../routes/web.php');
$this->loadRoutesFrom(__DIR__ . '/../routes/api.php');
// Load views
$this->loadViewsFrom(__DIR__ . '/../Resources/views', 'timetracker');
// Load migrations
$this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations');
// Load translations
$this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'timetracker');
// Publish assets
$this->publishes([
__DIR__ . '/../public/js' => public_path('assets/js/timetracker-plugin'),
], ['timetracker-assets', 'public']);
}
public function register(): void
{
// Register commands, services, etc.
}
}
loadRoutesFrom() - Register routesloadViewsFrom() - Register viewsloadMigrationsFrom() - Register migrationsloadTranslationsFrom() - Register translationspublishes() - Publish assets__DIR__ for pathsregister()
Define routes in routes/web.php with proper middleware and prefixes.
use Illuminate\Support\Facades\Route;
use Plugins\MyPlugin\Controllers\MyPluginController;
Route::middleware(['web', 'auth'])->prefix('my-plugin')->group(function () {
Route::get('/', [MyPluginController::class, 'index'])->name('my-plugin.index');
Route::post('/store', [MyPluginController::class, 'store'])->name('my-plugin.store');
});
namespace Plugins\MyPlugin\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class MyPluginController extends Controller
{
public function index()
{
return view('my-plugin::index');
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string',
]);
// Your logic here
return redirect()->route('my-plugin.index');
}
}
Create migrations in Database/Migrations/ following Laravel's naming convention.
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
public function up(): void
{
Schema::create('items', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('name');
$table->timestamps();
});
// Create permissions
DB::table('permissions')->insert([
['name' => 'manage_items', 'guard_name' => 'web', 'created_at' => now(), 'updated_at' => now()],
]);
}
public function down(): void
{
Schema::dropIfExists('items');
}
};
Render views using the namespace defined in your Service Provider.
return view('my-plugin::index', ['data' => $data]);
@extends('layouts.app')
@section('content')
{{ get_label('my_plugin', 'My Plugin') }}
{{ get_label('create', 'Create') }}
@endsection
get_label() for all text - never hardcode@extends('layouts.app') to inherit Taskify's layoutmy-plugin::
Create permissions in your migration's up() method.
DB::table('permissions')->insert([ ['name' => 'manage_items', 'guard_name' => 'web', 'created_at' => now(), 'updated_at' => now()], ['name' => 'create_items', 'guard_name' => 'web', 'created_at' => now(), 'updated_at' => now()], ]);
Route::middleware(['web', 'auth', 'customcan:manage_items'])->group(function () { // Routes here });
if (!isAdminOrHasAllDataAccess() && !auth()->user()->can('manage_items')) { abort(403); }
Register web and API routes that integrate seamlessly with the core system.
Add menu items to the main navigation with custom icons and permissions.
Create custom Blade views with full access to Taskify's layout system.
Build RESTful APIs with Laravel's routing and authentication system.
Register Artisan commands and schedule automated tasks with Laravel's scheduler.
Automatically publish JavaScript, CSS, and image assets to the public directory.
Join the ecosystem and extend Taskify with powerful, modular plugins. Get started in minutes with our comprehensive guide.
Open Plugin Guide