|
| 1 | +# Plugins |
| 2 | + |
| 3 | +Plugins are a powerful way to extend the functionality of your application without modifying the core codebase. They allow you to add new features without the need to modify the core application, making it easier to integrate custom functionality for different projects. |
| 4 | + |
| 5 | +## Plugin Structure |
| 6 | + |
| 7 | +Each plugin should be placed in the `app/Plugins/` directory with the following structure: |
| 8 | + |
| 9 | +``` |
| 10 | +app/Plugins/YourPlugin/ |
| 11 | +├── App/ |
| 12 | +│ ├── info.xml # Plugin metadata |
| 13 | +│ ├── permissions.json # (Optional) Plugin permissions |
| 14 | +│ └── role-presets.json # (Optional) Role presets |
| 15 | +├── composer.json # PHP dependencies |
| 16 | +├── package.json # JavaScript dependencies |
| 17 | +├── routes/ # (Optional) Plugin routes |
| 18 | +├── src/ # Frontend source files |
| 19 | +├── Scopes/ # (Optional) Model scopes |
| 20 | +├── js/ |
| 21 | +│ └── script.js # Compiled plugin script |
| 22 | +└── CHANGELOG.md # Version history |
| 23 | +``` |
| 24 | + |
| 25 | +## Features |
| 26 | + |
| 27 | +### PHP Dependencies |
| 28 | + |
| 29 | +Plugins can declare their own PHP dependencies using a standard `composer.json` file. The main application uses the [Composer Merge Plugin](https://github.com/wikimedia/composer-merge-plugin) to automatically discover and install all plugin dependencies when you run `composer install` or `composer update`. |
| 30 | + |
| 31 | +#### How It Works |
| 32 | + |
| 33 | +1. **Each plugin has its own `composer.json`** in its root directory |
| 34 | +2. **Dependencies are automatically merged** into the main application's composer.json |
| 35 | +3. **All packages are installed** in the main application's `vendor/` directory |
| 36 | +4. **Composer handles version resolution** automatically |
| 37 | + |
| 38 | +#### Creating a Plugin composer.json |
| 39 | + |
| 40 | +Create a `composer.json` file in your plugin's root directory: |
| 41 | + |
| 42 | +```json |
| 43 | +{ |
| 44 | + "name": "spacialist-plugin/your-plugin-name", |
| 45 | + "description": "Description of your plugin", |
| 46 | + "type": "library", |
| 47 | + "license": "MIT", |
| 48 | + "authors": [ |
| 49 | + { |
| 50 | + "name": "Your Name", |
| 51 | + "email": "your.email@example.com" |
| 52 | + } |
| 53 | + ], |
| 54 | + "require": { |
| 55 | + "php": ">=8.2.0", |
| 56 | + "guzzlehttp/guzzle": "^7.0", |
| 57 | + "monolog/monolog": "^3.0" |
| 58 | + }, |
| 59 | + "autoload": { |
| 60 | + "psr-4": { |
| 61 | + "App\\Plugins\\YourPluginName\\": "App/" |
| 62 | + } |
| 63 | + } |
| 64 | +} |
| 65 | +``` |
| 66 | + |
| 67 | +#### Important Notes |
| 68 | + |
| 69 | +- **Package naming**: Use the format `spacialist-plugin/your-plugin-name` (kebab-case) |
| 70 | +- **PHP version**: Must be compatible with the main application (>=8.2.0) |
| 71 | +- **Autoloading**: Follow PSR-4 standard with namespace `App\Plugins\YourPluginName\` |
| 72 | +- **Version constraints**: Use semantic versioning (e.g., `^7.0`, `~2.5`, `>=1.0.0`) |
| 73 | + |
| 74 | +#### Installing Dependencies |
| 75 | + |
| 76 | +After creating or modifying a plugin's `composer.json`, run: |
| 77 | + |
| 78 | +```bash |
| 79 | +composer update |
| 80 | +``` |
| 81 | + |
| 82 | +This will: |
| 83 | +- Discover all plugin `composer.json` files |
| 84 | +- Merge their dependencies |
| 85 | +- Resolve version conflicts |
| 86 | +- Install all packages to `vendor/` |
| 87 | + |
| 88 | +#### Version Conflicts |
| 89 | + |
| 90 | +If multiple plugins require different versions of the same package, Composer will: |
| 91 | +1. Attempt to find a compatible version that satisfies all constraints |
| 92 | +2. Display an error if no compatible version exists |
| 93 | +3. You'll need to update plugin requirements to compatible versions |
| 94 | + |
| 95 | +#### Example: Adding a New Dependency |
| 96 | + |
| 97 | +**Before:** |
| 98 | +```json |
| 99 | +{ |
| 100 | + "require": { |
| 101 | + "php": ">=8.2.0" |
| 102 | + } |
| 103 | +} |
| 104 | +``` |
| 105 | + |
| 106 | +**After:** |
| 107 | +```json |
| 108 | +{ |
| 109 | + "require": { |
| 110 | + "php": ">=8.2.0", |
| 111 | + "symfony/yaml": "^6.0", |
| 112 | + "league/csv": "^9.8" |
| 113 | + } |
| 114 | +} |
| 115 | +``` |
| 116 | + |
| 117 | +Then run `composer update` to install the new packages. |
| 118 | + |
| 119 | +### Metadata File (info.xml) |
| 120 | + |
| 121 | +The `App/info.xml` file contains essential plugin metadata: |
| 122 | + |
| 123 | +```xml |
| 124 | +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> |
| 125 | +<info> |
| 126 | + <name>YourPlugin</name> |
| 127 | + <title>Your Plugin Title</title> |
| 128 | + <description>Description of what your plugin does</description> |
| 129 | + <version>1.0.0</version> |
| 130 | + <licence>MIT</licence> |
| 131 | + <authors> |
| 132 | + <author>Your Name</author> |
| 133 | + </authors> |
| 134 | + <compatibility> |
| 135 | + <spacialist>0.12</spacialist> |
| 136 | + <php>8.2</php> |
| 137 | + </compatibility> |
| 138 | + <dependencies> |
| 139 | + <!-- List other plugins this plugin depends on --> |
| 140 | + </dependencies> |
| 141 | + <permissions> |
| 142 | + <!-- Declare what features your plugin uses --> |
| 143 | + <!-- <client /> --> |
| 144 | + <!-- <migration /> --> |
| 145 | + <!-- <routes /> --> |
| 146 | + <!-- <public /> --> |
| 147 | + </permissions> |
| 148 | + <accesspoints> |
| 149 | + <!-- Define custom routes/pages --> |
| 150 | + <!-- <accesspoint id="custom-page" label="Custom Page" path="/custom" /> --> |
| 151 | + </accesspoints> |
| 152 | + <scopes> |
| 153 | + <!-- Register model scopes --> |
| 154 | + <!-- <scope src="YourScope.php" on="App\Entity" /> --> |
| 155 | + </scopes> |
| 156 | + <attributes> |
| 157 | + <!-- Register custom attributes --> |
| 158 | + </attributes> |
| 159 | +</info> |
| 160 | +``` |
| 161 | + |
| 162 | +#### Key Fields |
| 163 | + |
| 164 | +- **name**: Internal identifier (must match directory name) |
| 165 | +- **title**: Display name shown in UI |
| 166 | +- **version**: Semantic version (major.minor.patch) |
| 167 | +- **compatibility**: Minimum required versions |
| 168 | +- **permissions**: Required features (migration, routes, etc.) |
| 169 | + |
| 170 | +### Permissions |
| 171 | + |
| 172 | +Define plugin-specific permissions in `App/permissions.json`: |
| 173 | + |
| 174 | +```json |
| 175 | +{ |
| 176 | + "plugin_group": [ |
| 177 | + { |
| 178 | + "name": "create", |
| 179 | + "display_name": "Create items", |
| 180 | + "description": "Allow creating new items" |
| 181 | + }, |
| 182 | + { |
| 183 | + "name": "delete", |
| 184 | + "display_name": "Delete items", |
| 185 | + "description": "Allow deleting items" |
| 186 | + } |
| 187 | + ] |
| 188 | +} |
| 189 | +``` |
| 190 | + |
| 191 | +Permissions are automatically registered during plugin installation. |
| 192 | + |
| 193 | +### Routes |
| 194 | + |
| 195 | +Create custom API routes in `routes/api.php`: |
| 196 | + |
| 197 | +```php |
| 198 | +<?php |
| 199 | + |
| 200 | +use Illuminate\Support\Facades\Route; |
| 201 | + |
| 202 | +Route::middleware('auth:sanctum')->group(function () { |
| 203 | + Route::get('/plugin-data', 'App\Plugins\YourPlugin\App\Controllers\DataController@index'); |
| 204 | + Route::post('/plugin-data', 'App\Plugins\YourPlugin\App\Controllers\DataController@store'); |
| 205 | +}); |
| 206 | +``` |
| 207 | + |
| 208 | +### Migrations |
| 209 | + |
| 210 | +Place database migrations in `App/Migrations/`: |
| 211 | + |
| 212 | +```php |
| 213 | +<?php |
| 214 | + |
| 215 | +use App\MigrationBase; |
| 216 | +use Illuminate\Database\Schema\Blueprint; |
| 217 | + |
| 218 | +return new class extends MigrationBase |
| 219 | +{ |
| 220 | + public function up() |
| 221 | + { |
| 222 | + $this->schema()->create('plugin_table', function (Blueprint $table) { |
| 223 | + $table->id(); |
| 224 | + $table->string('name'); |
| 225 | + $table->timestamps(); |
| 226 | + }); |
| 227 | + } |
| 228 | + |
| 229 | + public function down() |
| 230 | + { |
| 231 | + $this->schema()->dropIfExists('plugin_table'); |
| 232 | + } |
| 233 | +}; |
| 234 | +``` |
| 235 | + |
| 236 | +### Model Scopes |
| 237 | + |
| 238 | +Add global scopes in `Scopes/` and register them in `info.xml`: |
| 239 | + |
| 240 | +```php |
| 241 | +<?php |
| 242 | + |
| 243 | +namespace App\Plugins\YourPlugin\Scopes; |
| 244 | + |
| 245 | +use Illuminate\Database\Eloquent\Builder; |
| 246 | +use Illuminate\Database\Eloquent\Model; |
| 247 | +use Illuminate\Database\Eloquent\Scope; |
| 248 | + |
| 249 | +class YourScope implements Scope |
| 250 | +{ |
| 251 | + public function apply(Builder $builder, Model $model) |
| 252 | + { |
| 253 | + // Apply your scope logic |
| 254 | + } |
| 255 | +} |
| 256 | +``` |
| 257 | + |
| 258 | +Register in `info.xml`: |
| 259 | +```xml |
| 260 | +<scopes> |
| 261 | + <scope src="YourScope.php" on="App\Entity" /> |
| 262 | +</scopes> |
| 263 | +``` |
| 264 | + |
| 265 | +### Frontend Integration |
| 266 | + |
| 267 | +Build your frontend with Vite and export it to `js/script.js`. The plugin system will automatically load this file when the plugin is activated. |
| 268 | + |
| 269 | +### Changelog |
| 270 | + |
| 271 | +Maintain a `CHANGELOG.md` file to track version history: |
| 272 | + |
| 273 | +```markdown |
| 274 | +# Changelog |
| 275 | + |
| 276 | +## Version 1.1.0 - 2026-02-09 |
| 277 | +### Added |
| 278 | +- New feature X |
| 279 | +- Support for Y |
| 280 | + |
| 281 | +### Fixed |
| 282 | +- Bug in Z |
| 283 | + |
| 284 | +## Version 1.0.0 - 2026-01-01 |
| 285 | +- Initial release |
| 286 | +``` |
| 287 | + |
| 288 | +## Development Workflow |
| 289 | + |
| 290 | +1. **Create plugin directory** in `app/Plugins/YourPlugin` |
| 291 | +2. **Add composer.json** with your PHP dependencies |
| 292 | +3. **Add package.json** with your JavaScript dependencies |
| 293 | +4. **Create info.xml** with plugin metadata |
| 294 | +5. **Run composer update** to install PHP dependencies |
| 295 | +6. **Run npm install** to install JavaScript dependencies |
| 296 | +7. **Develop your plugin** (backend, frontend, migrations, etc.) |
| 297 | +8. **Build frontend**: `npm run build` |
| 298 | +9. **Test your plugin** in development mode |
| 299 | +10. **Install via admin interface** when ready |
| 300 | + |
| 301 | +## Best Practices |
| 302 | + |
| 303 | +- **Use semantic versioning** for your plugin versions |
| 304 | +- **Document breaking changes** in CHANGELOG.md |
| 305 | +- **Specify minimum PHP/Spacialist versions** in info.xml |
| 306 | +- **Use PSR-4 autoloading** for PHP classes |
| 307 | +- **Keep dependencies minimal** to avoid conflicts |
| 308 | +- **Test with different plugin combinations** |
| 309 | +- **Provide clear installation instructions** |
| 310 | + |
| 311 | +## Troubleshooting |
| 312 | + |
| 313 | +### Dependency Conflicts |
| 314 | + |
| 315 | +If you encounter version conflicts: |
| 316 | + |
| 317 | +```bash |
| 318 | +composer update --with-all-dependencies |
| 319 | +``` |
| 320 | + |
| 321 | +Check which plugins require conflicting versions: |
| 322 | +```bash |
| 323 | +composer why vendor/package |
| 324 | +composer why-not vendor/package 2.0 |
| 325 | +``` |
| 326 | + |
| 327 | +### Autoloading Issues |
| 328 | + |
| 329 | +If classes aren't found after adding to composer.json: |
| 330 | + |
| 331 | +```bash |
| 332 | +composer dump-autoload |
| 333 | +``` |
| 334 | + |
| 335 | +### Plugin Not Detected |
| 336 | + |
| 337 | +1. Check that `info.xml` is in `App/` directory |
| 338 | +2. Verify XML is valid |
| 339 | +3. Ensure directory name matches `<name>` in info.xml |
| 340 | +4. Refresh plugin state in admin interface |
0 commit comments