Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/6.x/extend/approach.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Some low-level plugins may no longer be necessary in the Laravel ecosystem, but
Examples of this are:

- **Mail transport adapters** — Projects can set up mailers via `config/mail.php` and select one in <Journey path="Settings, Email" />.
- **Custom log back-ends** — As with prior versions of Craft, plugins may not be initialized early enough in the app’s lifecycle to capture a complete picture. Consider whether projects can effectively use Laravel’s built-in [logging](laravellogging) tools.
- **Custom log back-ends** — As with prior versions of Craft, plugins may not be initialized early enough in the app’s lifecycle to capture a complete picture. Consider whether projects can effectively use Laravel’s built-in [logging](laravel:logging) tools.

::: warning
Filesystems have been completely removed in Craft 6.x.
Expand Down
4 changes: 2 additions & 2 deletions docs/6.x/extend/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class GenerateReport extends Command
}
```

Your command’s [arguments and options](artisan#defining-input-expectations) are determined by its _signature_.
Your command’s [arguments and options](laravel:artisan#defining-input-expectations) are determined by its _signature_.
Laravel takes care of matching command arguments with those of the `handle()` method; other arguments are resolved with dependency injection.
In this example, `$manager` isn’t an argument that we’d expect a user to supply—it’s just a [service](services.md) we want access to in that scope.

Expand All @@ -59,7 +59,7 @@ public array $commands = [

## Command Closure

You can also create simple commands directly in your plugin’s `boot()` method, using a [laravel:artisan#closure-commands]:
You can also create simple commands directly in your plugin’s `boot()` method, using a [closure command](laravel:artisan#closure-commands):

```php
Artisan::command('report:generate {templateId}', function (Manager $manager, int $templateId) {
Expand Down
2 changes: 1 addition & 1 deletion docs/6.x/extend/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public function rulesClass(): string
```

You may also tag the settings model with the `#[Ruleset(MyPluginSettingsRuleset::class)]` attribute.
For more control over how your settings data is normalized, return your own [form request](validation#form-request-validation) class from `getSettingsRequestClass()`.
For more control over how your settings data is normalized, return your own [form request](validation.md#form-request-validation) class from `getSettingsRequestClass()`.

When using a custom `FormRequest`, your rules are evaluated against the entire request, not just the nested `settings` key that Craft ordinarily assigns to your settings model.
The example above (validating `adminNotificationsAddress` on the settings model) would need to be updated to `settings.adminNotificationsAddress` if returned from `FormRequest::rules()`.
Expand Down
23 changes: 22 additions & 1 deletion docs/6.x/extend/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,27 @@ Laravel’s “terminable” middleware is not compatible with all hosting envir
app()->terminating($this->flushLazyEvents());
```

### CSRF

Laravel verifies CSRF tokens on web routes, so a form posted without a token is rejected with a 419.
Front-end templates can keep using `{{ csrfInput() }}`, and Ajax requests can send the `XSRF-TOKEN` cookie value in an `X-XSRF-TOKEN` header.

If an endpoint must accept posts without a token (a webhook, or a plugin that used to disable `enableCsrfValidation` on a Yii controller), exempt its URI from your plugin’s `boot()` method:

```php
use CraftCms\Cms\Route\Routes;
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;

public function boot(Routes $routes): void
{
PreventRequestForgery::except([
$routes->actionTriggerUriPrefix()."/$this->handle/save",
]);
}
```

Craft uses the same mechanism for its own `graphql/api` action.

### Authorization

Controllers in Laravel don’t often do their own authorization, because they’re no longer automatically exposed to the router.
Expand Down Expand Up @@ -209,7 +230,7 @@ In some situations, you may still need to resolve permissions directly via a use

```php
// Check against the current user:
Auth::craftUser()->can('triggerReport');
currentUserElement()->can('triggerReport');

// Check against a specific user:
$recipient->can('requestAccess', $report);
Expand Down
2 changes: 1 addition & 1 deletion docs/6.x/extend/local-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ If the host project doesn’t include the recommended `scripts` for a Laravel ap
```bash
ddev composer require myorg/activity-plugin

# New: Ask Laravel to
# New: Ask Laravel to discover the package
ddev artisan package:discover
```

Expand Down
16 changes: 16 additions & 0 deletions docs/6.x/extend/provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ Plugins now extend `CraftCms\Cms\Plugin\Plugin`, and are Laravel [service provid
Many plugins will only need to update their [settings model](#settings) and rename the `init()` method to `boot()`.
At this point—with the adapter installed—your plugin should be in a stable state from which you can gradually move [initialization logic](avenues.md#initialization) to the new architecture.

::: warning
The adapter only routes `actions/{handle}/…` URLs to plugins that extend the legacy `craft\base\Plugin` class.
Once your plugin extends `CraftCms\Cms\Plugin\Plugin`, any Yii controllers it ships return a 404 until you port them to Laravel controllers and [route](http.md#action-paths) them in `routes/actions.php`.
Switch the base class together with your controllers, and test your action URLs afterwards.
The settings page also changes: `settingsHtml()` is not used by the new base class, so implement `settingsForm()` (see [settings](config.md#settings-form)).
:::

You are not obligated to adopt `PascalCase` namespaces, but if you do (and your base class is named something other than `Plugin`, in your autoloading root), make the corresponding update to the `extra.class` property in `composer.json`:

```json{8}
Expand All @@ -27,6 +34,15 @@ In order to support the Craft plugin lifecycle (installation and uninstallation,
## Settings

The `createSettings()` method replaces `createSettingsModel()`, and must return a subclass of `CraftCms\Cms\Plugin\PluginSettings` (or `null`).
Note that it is a **static** method:

```php
protected static function createSettings(): ?PluginSettings
{
return new Settings();
}
```

This base class retains much of the same [component](models.md#data-objects) functionality, and should be a drop-in replacement for `craft\base\Model`.

::: tip
Expand Down
2 changes: 1 addition & 1 deletion docs/6.x/extend/session.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The session is deeply integrated with validation, templating, controllers, and a

## Auth

You can always get the current `User` element with `Illuminate\Support\Facades\Auth::craftUser()`.
You can always get the current user with the `craftUser()` method on the request (`$request->craftUser()`), or the `CraftCms\Cms\currentUser()` and `CraftCms\Cms\currentUserElement()` helper functions.
Users are largely unchanged, but the class has been relocated to `CraftCms\Cms\User\Elements\User` and some of its functionality has been split out into concerns (or is handled entirely by Laravel).

## Flashes
Expand Down
25 changes: 25 additions & 0 deletions docs/6.x/extend/structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,31 @@ Within your plugin’s `src/` directory, you are not obligated to use any partic
While some conventions are similar, Laravel’s opinionated paths (i.e. `Listeners/` for auto-registered [event](events.md) handlers) are only relevant in the project’s space (`app/`), and have no bearing on plugins.
:::

## Class Organization

You are free to organize `src/` however you like, but we recommend following the conventions that Craft’s own code (and most Laravel packages) use: a PascalCase PSR-4 namespace (like `CraftCms\MyPlugin`), with classes grouped by what they _are_.
Craft groups by feature first, and by role within each feature.

| Kind of class | Location | Core example |
| --- | --- | --- |
| Plugin class and settings | `src/Plugin.php`, `src/Settings.php` | |
| Controllers | `src/Http/Controllers/` | `CraftCms\Cms\Http\Controllers\AddressesController` |
| Form requests | `src/Http/Requests/` | |
| Middleware | `src/Http/Middleware/` | |
| Events | `src/Events/` | `CraftCms\Cms\Section\Events\SectionSaved` |
| Listeners | `src/Listeners/` | |
| Data objects | `src/Data/` | `CraftCms\Cms\Section\Data\Section` |
| Eloquent models (formerly records) | `src/Models/` | `CraftCms\Cms\Section\Models\Section` |
| Enums | `src/Enums/` | `CraftCms\Cms\Section\Enums\SectionType` |
| Exceptions | `src/Exceptions/` | `CraftCms\Cms\Section\Exceptions\SectionNotFoundException` |
| Jobs, commands, policies | `src/Jobs/`, `src/Console/Commands/`, `src/Policies/` | |
| Services | A feature folder, with `#[Singleton]` classes | `CraftCms\Cms\Section\Sections` |

Larger plugins can nest these inside a feature folder (`src/Reporting/Events/`, `src/Reporting/Data/`).

When porting a Craft 5 plugin, this usually means moving classes out of Yii-style lowercase folders (`controllers/`, `models/`, `events/`, `services/`) as you port them.
Directory names are case-sensitive, so make sure each namespace matches its path exactly, and list every renamed class in your upgrade notes.

## Plugin Base Class

Craft needs to be able to locate your base class.
Expand Down
6 changes: 6 additions & 0 deletions docs/6.x/extend/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ The legacy base plugin class (`craft\base\Plugin`) and the interfaces it impleme
We have mainly observed this with the `attributes()` and `attributeLabels()` methods.
If you don’t use these, there’s nothing to do here.

::: tip
This applies while you keep extending `craft\base\Plugin` and `craft\base\Model`.
The new `CraftCms\Cms\Plugin\PluginSettings` class extends `CraftCms\Cms\Component\Component`, which has no `attributes()` method, so a settings class that moves to it can’t call `parent::attributes()`.
Expose values as public properties instead.
:::

<Block label="Wait… is that it?">

Basically, _yes_!
Expand Down
Loading