Xgenious/ docs

Anatomy and Manifest

Folder layout

core/plugins/MyPlugin/
├── plugin.json              # manifest (required)
├── MyPluginPlugin.php       # provider class (required)
├── Http/                    # controllers
├── views/                   # Blade views
├── Console/                 # Artisan commands
├── Models/                  # Eloquent models
├── public/                  # optional static files served by the plugin-assets route
│   └── css/app.css
└── database/migrations/     # optional; run on enable

Only plugin.json and the provider class are required. Everything else is up to you. Composer already maps "plugins\\": "plugins/", so provider and helper classes autoload without running composer dump-autoload.

The manifest

plugin.json is validated by App\Plugins\PluginManifestValidator. A plugin with an invalid manifest is logged and never registered.

{
  "id": "store-import",
  "name": "Store Import",
  "version": "1.0.0",
  "description": "Teach your chatbot your product catalog.",
  "help": "Open Store Import in the user dashboard and enter your store URL.",
  "provider": "plugins\\StoreImport\\StoreImportPlugin",
  "namespace": "plugins\\StoreImport\\",
  "requires": { "helpnest": ">=1.0" },
  "capabilities": ["user-menu", "routes", "commands"]
}
FieldRule
idRequired. Lowercase letters, numbers and hyphens only (^[a-z0-9\-]+$). Unique across all plugins. Also used in route prefixes and route names.
nameRequired. Shown in the admin Plugins screen.
versionRequired. A semver string.
providerRequired. Fully qualified class name. The class must implement PluginContract.
namespaceRequired. The PSR-4 prefix of the plugin.
description, helpOptional. Shown to admins.
requires.helpnest, requires.phpOptional version constraints.
depends, permissions, capabilities, checksumOptional arrays.
Tip

The capabilities array documents what your plugin touches (for example user-menu, routes, commands, hooks). It is informational, so keep it honest for the admin reading it.

The provider class

The provider is the plugin's entry point. It must implement App\Plugins\Contracts\PluginContract:

<?php

namespace plugins\MyPlugin;

use App\Plugins\Contracts\PluginContract;
use App\Plugins\Contracts\PluginContextContract;

class MyPlugin implements PluginContract
{
    public function register(PluginContextContract $context): void {}   // container bindings, view namespaces
    public function boot(PluginContextContract $context): void {}       // hooks, menus, routes, assets, schedules
    public function activate(PluginContextContract $context): void {}   // runs once when the plugin is enabled
    public function deactivate(PluginContextContract $context): void {} // runs when disabled
    public function uninstall(PluginContextContract $context): void {}  // runs when uninstalled; clean up your data here
}
MethodUse it for
registerBinding services into the container and registering a Blade view namespace. Keep it side-effect free.
bootEverything that wires the plugin into HelpNest: menus, routes, hooks, event listeners, scheduled commands, assets.
activateOne-time setup that is not a migration.
deactivateRemove anything boot would otherwise leave behind. Data is kept.
uninstallRemove your plugin's data and settings.

Plugin code is only booted when its row is active, so you never need to check that yourself.

The context object

Every method receives a PluginContext. It is how a plugin talks to the framework:

CallPurpose
$context->hooks()Add actions and filters.
$context->menus()Add menu items.
$context->assets()Register CSS and JS.
$context->routes()Register routes.
$context->settings()Read and write plugin options.
$context->permissions()Names of the permissions your manifest declares.
$context->logger()A logger for your plugin.
$context->commands([...])Register Artisan commands.
$context->path('views')Absolute path inside your plugin folder.
$context->url('...')Public URL for a file in your plugin's public/ directory.

Database migrations

Put migrations in database/migrations/. They run when the plugin is enabled and are tracked in the plugin_migrations table, so each runs once. Prefix table names with your plugin's name (for example store_import_stores) to avoid clashing with core or other plugins.

Still stuck?
Our support team is ready to help you get set up.
Get support