Build powerful extensions for Taskify with our comprehensive plugin architecture. Create seamless integrations that enhance productivity and extend functionality.
Taskify supports a robust plugin architecture that allows developers to extend the core functionality of the application. This guide will walk you through creating plugins that seamlessly integrate with Taskify's ecosystem.
Modular design for easy integration and management
Built on Laravel framework with full MVC support
Add custom features without modifying core code
A Taskify plugin follows a specific directory structure. Here's the standard layout:
plugins/
└── YourPluginName/
├── Console/
│ └── CleanupCommand.php
├── Controllers/
│ └── YourPluginController.php
├── Database/
│ └── Migrations/
│ ├── 2024_01_01_000000_create_your_plugin_table.php
│ └── 2024_01_01_000001_create_related_table.php
├── Models/
│ ├── YourModel.php
│ └── RelatedModel.php
├── Providers/
│ └── YourPluginServiceProvider.php
├── Resources/
│ └── lang/
│ └── en/
│ └── plugin_labels.php
│ └── views/
│ ├── feature1/
│ ├── feature2/
│ └── main/
├── routes/
│ ├── api.php
│ ├── web.php
│ └── menus.php
├── plugin.json
└── README.md
Every plugin must have a plugin.json file in its root directory:
{
"name": "Your Plugin Name - Feature Description",
"slug": "your-plugin-slug",
"description": "Brief description of functionality.",
"version": "1.0.0",
"enabled": true,
"provider": "Plugins\\YourPluginName\\Providers\\YourPluginServiceProvider",
"publish_tag": "your-plugin-assets"
}
| Field | Description |
|---|---|
name |
Full descriptive name |
slug |
URL-friendly identifier |
description |
Brief functionality explanation |
version |
Semantic version number |
enabled |
Boolean to enable/disable |
provider |
Service provider class name |
publish_tag |
Tag for publishing assets |
Handle HTTP requests and business logic
Represent plugin data structures
Create maintenance and utility commands
Create database migrations for your plugin tables:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('your_plugin_table', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('your_plugin_table');
}
};
Follow Laravel's migration naming convention:
YYYY_MM_DD_HHMMSS_create_table_name.phpdown() methodResources/
└── views/
├── layouts/
│ └── app.blade.php
├── components/
│ └── form-input.blade.php
├── index.blade.php
├── create.blade.php
├── edit.blade.php
└── show.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
<h1>{{ __('yourplugin::plugin_labels.title') }}</h1>
<div class="card">
<div class="card-body">
<table class="table">
<thead>
<tr>
<th>{{ __('yourplugin::plugin_labels.name') }}</th>
<th>{{ __('yourplugin::plugin_labels.actions') }}</th>
</tr>
</thead>
<tbody>
{{-- Data will be loaded here --}}
</tbody>
</table>
</div>
</div>
</div>
@endsection
Create language files for internationalization:
<?php
// Resources/lang/en/plugin_labels.php
return [
'title' => 'Your Plugin Title',
'create_new' => 'Create New',
'name' => 'Name',
'description' => 'Description',
'created_at' => 'Created At',
'actions' => 'Actions',
'edit' => 'Edit',
'delete' => 'Delete',
'save' => 'Save',
'cancel' => 'Cancel',
'success_message' => 'Operation completed successfully',
'error_message' => 'An error occurred',
];
Define your plugin routes:
<?php
// routes/web.php
use Illuminate\Support\Facades\Route;
use Plugins\YourPluginName\Controllers\YourPluginController;
Route::prefix('yourplugin')->group(function () {
Route::get('/', [YourPluginController::class, 'index'])
->name('yourplugin.index');
Route::post('/', [YourPluginController::class, 'store'])
->name('yourplugin.store');
Route::get('/{id}', [YourPluginController::class, 'show'])
->name('yourplugin.show');
Route::put('/{id}', [YourPluginController::class, 'update'])
->name('yourplugin.update');
Route::delete('/{id}', [YourPluginController::class, 'destroy'])
->name('yourplugin.destroy');
});
public function store(Request $request)
{
try {
$request->validate([
'name' => 'required|string|max:255',
]);
$item = YourModel::create($request->all());
return response()->json([
'success' => true,
'data' => $item,
'message' => __('yourplugin::plugin_labels.success_message')
]);
} catch (\Exception $e) {
return response()->json([
'success' => false,
'message' => __('yourplugin::plugin_labels.error_message')
], 500);
}
}
Let's walk through creating a "Plugin Example" following the Taskify structure
Create the plugin directory structure
plugins/ExamplePlugin/
├── Controllers/
├── Models/
├── Database/Migrations/
├── Resources/views/
├── Resources/lang/
├── routes/
├── Providers/
└── plugin.json
Set up the plugin.json file
{
"name": "Taskify - Example Plugin",
"slug": "example-plugin",
"description": "Example functionality",
"version": "1.0.0",
"enabled": true,
"provider": "Plugins\\ExamplePlugin\\Providers\\ExamplePluginServiceProvider",
"publish_tag": "example-plugin-assets"
}
Create the plugin model
class ExampleModel extends Model
{
protected $fillable = [
'task_id',
'content',
'user_id',
'is_active'
];
public function task()
{
return $this->belongsTo(Task::class);
}
}
<?php
namespace Plugins\ExamplePlugin\Controllers;
use App\Http\Controllers\Controller;
use Plugins\ExamplePlugin\Models\ExampleModel;
use Illuminate\Http\Request;
class ExamplePluginController extends Controller
{
public function index($taskId)
{
$items = ExampleModel::where('task_id', $taskId)
->with('user')
->orderBy('created_at', 'desc')
->get();
return view('exampleplugin::feature_one.index', compact('items', 'taskId'));
}
public function store(Request $request)
{
$request->validate([
'task_id' => 'required|exists:tasks,id',
'content' => 'required|string',
'is_active' => 'boolean',
]);
$item = ExampleModel::create([
'task_id' => $request->task_id,
'content' => $request->content,
'user_id' => auth()->id(),
'is_active' => $request->is_active ?? true,
]);
return response()->json([
'success' => true,
'item' => $item->load('user'),
'message' => __('exampleplugin::plugin_labels.item_created')
]);
}
}
Create unit tests for your plugin components:
<?php
namespace Tests\Plugins\YourPluginName;
use Tests\TestCase;
use Plugins\YourPluginName\Models\YourModel;
class YourModelTest extends TestCase
{
public function testCanCreateModel()
{
$model = YourModel::create([
'name' => 'Test Name',
'description' => 'Test Description',
]);
$this->assertInstanceOf(YourModel::class, $model);
$this->assertEquals('Test Name', $model->name);
}
}
Test your plugin's HTTP endpoints:
<?php
namespace Tests\Plugins\YourPluginName;
use Tests\TestCase;
class YourPluginControllerTest extends TestCase
{
public function testCanViewIndex()
{
$response = $this->get('/yourplugin');
$response->assertStatus(200);
}
public function testCanCreateItem()
{
$response = $this->post('/yourplugin', [
'name' => 'Test Item',
'description' => 'Test Description',
]);
$response->assertStatus(201);
$response->assertJson(['success' => true]);
}
}
Copy your plugin directory to plugins/YourPluginName/
php artisan migrate
php artisan vendor:publish --tag=your-plugin-assets
php artisan cache:clear && php artisan config:clear
provider path in plugin.json matches your actual service provider class
Database/Migrations directory
For plugin updates:
plugin.jsonphp artisan migratephp artisan vendor:publish --tag=your-plugin-assets --forcephp artisan cache:clearYou now have a comprehensive understanding of Taskify plugin development. The modular architecture allows for powerful extensions while maintaining clean separation of concerns.