Plugin Structure 
Introduction 
Plugins in Botble CMS follow a modular architecture that allows for easy extension of the core functionality. Each plugin is a self-contained module with its own controllers, models, views, and assets.
All plugins are registered to Composer autoloader manually. Each plugin requires a plugin.json file to provide all needed information for auto-loading.
Directory Structure 
When you create a new plugin using the php artisan cms:plugin:create command, it generates the following directory structure:
platform/plugins/foo/
├── config/
│   └── permissions.php
├── database/
│   ├── migrations/
│   └── seeders/
├── helpers/
├── resources/
│   ├── assets/
│   ├── lang/
│   └── views/
├── routes/
│   └── web.php
├── src/
│   ├── Forms/
│   ├── Http/
│   │   ├── Controllers/
│   │   └── Requests/
│   ├── Models/
│   ├── Providers/
│   │   └── FooServiceProvider.php
│   ├── Repositories/
│   └── Plugin.php
└── plugin.jsonKey Components 
plugin.json 
This file contains metadata about your plugin, including its name, namespace, provider, author, and description. It's required for every plugin and serves as the plugin's manifest.
Example:
{
    "name": "Foo",
    "namespace": "Botble\\Foo\\",
    "provider": "Botble\\Foo\\Providers\\FooServiceProvider",
    "author": "Your Name",
    "url": "https://yourwebsite.com",
    "version": "1.0",
    "description": "A simple foo plugin for Botble CMS",
    "minimum_core_version": "7.3.0"
}The fields in the plugin.json file are:
name: The display name of your pluginnamespace: The PHP namespace of your plugin (must end with a double backslash)provider: The fully qualified class name of your plugin's service providerauthor: The name of the plugin authorurl: The website URL of the plugin or authorversion: The version number of the plugindescription: A brief description of what the plugin doesminimum_core_version: The minimum version of Botble CMS required to run this plugin
Plugin.php 
This file handles the plugin lifecycle events: activation, deactivation, and removal. It implements the PluginInterface or extends PluginOperationAbstract.
Example:
<?php
namespace Botble\Foo;
use Botble\PluginManagement\Abstracts\PluginOperationAbstract;
use Illuminate\Support\Facades\Schema;
use Botble\Setting\Facades\Setting;
class Plugin extends PluginOperationAbstract
{
    public static function activate(): void
    {
        // Logic to run when the plugin is activated
        // For example, you might want to set default settings
        Setting::set('foo_items_per_page', 10)->save();
    }
    public static function deactivate(): void
    {
        // Logic to run when the plugin is deactivated
    }
    public static function remove(): void
    {
        // Clean up when the plugin is removed
        Schema::dropIfExists('foo_items');
        Schema::dropIfExists('foo_categories');
        // Remove plugin settings
        Setting::delete(['foo_items_per_page', 'foo_display_author']);
    }
}The Plugin class has three main methods:
activate(): Called when the plugin is activated. Use this to initialize any settings or data your plugin needs.deactivate(): Called when the plugin is deactivated but not removed. Use this to temporarily disable functionality.remove(): Called when the plugin is completely removed. Use this to clean up database tables, settings, and any other data created by your plugin.
Configuration Files 
permissions.php 
Each plugin should have a configuration for permissions. Permissions are defined in code so we need to specify them in this file.
Example:
return [
    [
        'name' => 'Foo',
        'flag' => 'foo.index',
    ],
    [
        'name' => 'Create',
        'flag' => 'foo.create',
        'parent_flag' => 'foo.index',
    ],
    [
        'name' => 'Edit',
        'flag' => 'foo.edit',
        'parent_flag' => 'foo.index',
    ],
    [
        'name' => 'Delete',
        'flag' => 'foo.destroy',
        'parent_flag' => 'foo.index',
    ],
];The permissions configuration consists of an array of permission definitions:
name: The display name of the permissionflag: The unique identifier for the permission, typically following the formatplugin.actionparent_flag: Optional parent permission flag for hierarchical permissions
Service Provider 
The service provider is the main entry point of your plugin. It registers routes, views, translations, and other resources. A plugin must have this file.
Example:
<?php
namespace Botble\Foo\Providers;
use Botble\Base\Facades\DashboardMenu;
use Botble\Base\Traits\LoadAndPublishDataTrait;
use Botble\Foo\Models\Item;
use Illuminate\Support\ServiceProvider;
class FooServiceProvider extends ServiceProvider
{
    use LoadAndPublishDataTrait;
    public function register(): void
    {
        // Load helpers
        $this->app->make('files')->requireOnce(__DIR__ . '/../../helpers/constants.php');
        $this->app->make('files')->requireOnce(__DIR__ . '/../../helpers/helpers.php');
    }
    public function boot(): void
    {
        $this
            ->setNamespace('plugins/foo') // Set namespace for views, translations, etc.
            ->loadHelpers() // Load all helpers in the helpers folder
            ->loadAndPublishConfigurations(['permissions']) // Load and publish config files
            ->loadMigrations() // Load migrations
            ->loadAndPublishTranslations() // Load and publish translations
            ->loadAndPublishViews() // Load and publish views
            ->loadRoutes(['web']); // Load routes with web middleware
        // Register menu items in the admin dashboard
        DashboardMenu::default()->beforeRetrieving(function (): void {
            DashboardMenu::make()
                ->registerItem([
                    'id' => 'cms-plugins-foo', // Unique ID for the menu item
                    'priority' => 5, // Position in the menu
                    'parent_id' => null, // Parent ID for submenu items
                    'name' => 'plugins/foo::foo.name', // Translation key for the menu name
                    'icon' => 'ti ti-box', // Icon for the menu item
                    'url' => route('foo.index'), // URL for the menu item
                    'permissions' => ['foo.index'], // Required permissions to see this menu
                ]);
        });
        // Add additional hooks, filters, or actions here
        $this->app->booted(function () {
            // Code to run after the application is fully booted
        });
    }
}The service provider has two main methods:
register(): Used to bind implementations to the service container. This is where you register repositories, services, and load helpers.boot(): Called after all other service providers have been registered. This is where you load and publish assets, register routes, and add menu items.
The LoadAndPublishDataTrait provides several helpful methods:
setNamespace(): Sets the namespace for views, translations, and other assetsloadHelpers(): Loads all PHP files from the helpers directoryloadAndPublishConfigurations(): Loads and publishes configuration filesloadMigrations(): Loads database migrationsloadAndPublishTranslations(): Loads and publishes translation filesloadAndPublishViews(): Loads and publishes view filesloadRoutes(): Loads route files
Best Practices 
Naming Conventions 
- Plugin Name: Use PascalCase for plugin names (e.g., 
Foo,Ecommerce) - Database Tables: Prefix tables with your plugin name (e.g., 
foo_items,foo_categories) - Routes: Use kebab-case for route names (e.g., 
foo.items.create) - Translation Keys: Use dot notation and lowercase (e.g., 
plugins/foo::items.create) - Constants: Use SCREAMING_SNAKE_CASE for constants (e.g., 
FOO_MODULE_SCREEN_NAME) - Classes: Use PascalCase for class names (e.g., 
ItemController,FooServiceProvider) - Methods: Use camelCase for method names (e.g., 
getItems(),createItem()) - Variables: Use snake_case for variables (e.g., 
$item_count,$user_id) 
