Taskify Plugin Development Guide

Build powerful extensions for Taskify with our comprehensive plugin architecture. Create seamless integrations that enhance productivity and extend functionality.

Introduction

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.

Plugin Architecture

Modular design for easy integration and management

Laravel Integration

Built on Laravel framework with full MVC support

Extensible

Add custom features without modifying core code

Plugin Structure

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
Note: This structure ensures consistency across all plugins and makes them easy to maintain and debug.

Getting Started

1. Plugin Configuration File

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"
}

Configuration Fields

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

Core Components

Controllers

Handle HTTP requests and business logic

Models

Represent plugin data structures

Console Commands

Create maintenance and utility commands

Database Integration

Migration Example

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');
    }
};

Migration Best Practices

Naming Convention

Follow Laravel's migration naming convention:

  • YYYY_MM_DD_HHMMSS_create_table_name.php
  • Use descriptive names
  • Include foreign key constraints
  • Add proper indexes
Important Notes
  • Always include rollback logic in down() method
  • Test migrations in development environment
  • Use nullable fields when appropriate

Views & Templates

View Structure

Resources/
└── views/
    ├── layouts/
    │   └── app.blade.php
    ├── components/
    │   └── form-input.blade.php
    ├── index.blade.php
    ├── create.blade.php
    ├── edit.blade.php
    └── show.blade.php

Sample Blade Template

@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

Resources & Localization

Language Files

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',
];

Route Configuration

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');
});

Best Practices

Naming Conventions
  • Use PascalCase for class names
  • Use snake_case for database table names
  • Use kebab-case for route names
  • Prefix all plugin resources
Security
  • Always validate input data
  • Use Laravel's built-in security features
  • Implement proper authentication
  • Sanitize output data
Error Handling
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);
    }
}
Performance
  • Use database indexes appropriately
  • Implement caching where beneficial
  • Optimize database queries
  • Use Laravel's optimization tools

Example Plugin Walkthrough

Let's walk through creating a "Plugin Example" following the Taskify structure

Step 1: Structure

Create the plugin directory structure

plugins/ExamplePlugin/
├── Controllers/
├── Models/
├── Database/Migrations/
├── Resources/views/
├── Resources/lang/
├── routes/
├── Providers/
└── plugin.json
Step 2: Configuration

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"
}
Step 3: Model

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);
    }
}

Complete Controller Example

<?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')
        ]);
    }
}

Testing Your Plugin

Unit Tests

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);
    }
}

Feature Tests

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]);
    }
}
Testing Best Practices
  • Write tests for all critical functionality
  • Test both success and failure scenarios
  • Use database transactions for test isolation
  • Mock external dependencies

Deployment

Installation Steps

  1. 1
    Copy Plugin

    Copy your plugin directory to plugins/YourPluginName/

  2. 2
    Run Migrations

    php artisan migrate

  3. 3
    Publish Assets

    php artisan vendor:publish --tag=your-plugin-assets

  4. 4
    Clear Caches

    php artisan cache:clear && php artisan config:clear

Troubleshooting

Check that the provider path in plugin.json matches your actual service provider class

Ensure the view namespace in your service provider matches the one used in controllers

Verify the migration files are in the correct Database/Migrations directory
Plugin Updates

For plugin updates:

  1. Replace the plugin files with the new version
  2. Update the version in plugin.json
  3. Run migrations: php artisan migrate
  4. Republish assets: php artisan vendor:publish --tag=your-plugin-assets --force
  5. Clear caches: php artisan cache:clear

Congratulations!

You now have a comprehensive understanding of Taskify plugin development. The modular architecture allows for powerful extensions while maintaining clean separation of concerns.