### Creating a Facade for a Service in Craft 6.x Source: https://craftcms.com/docs/6.x/extend/services Provides an example of creating a facade class to simplify access to a service, similar to how facades are used in Laravel. ```php namespace MyOrg\Activity\Facades; use Illuminate\Support\Facades\Facade; class Reports extends Facade { #["\Override"] protected static function getFacadeAccessor(): string { return \MyOrg\Activity\Reporting\Manager::class; } } # -> Reports::generate(...) ``` -------------------------------- ### Service Fork Method Implementation Source: https://craftcms.com/docs/6.x/extend/services Example implementation of a 'fork' method within a service class, demonstrating how it can accept dependencies like 'Elements'. ```php class Manager { public function fork( Template $original, ?User $creator = null, Elements $elements, ): Template { return $elements->duplicate($original, ['creator' => $creator ?? $original->creator]); } } ``` -------------------------------- ### Require the Yii2 Adapter Package Source: https://craftcms.com/docs/6.x/extend/adapter Install the adapter package using Composer. Keep it as a dependency of your package as long as you need it. ```bash composer require craftcms/yii2-adapter ``` -------------------------------- ### Single-Action Controller with Dependency Injection Source: https://craftcms.com/docs/6.x/extend/http Example of a single-action controller demonstrating dependency injection via its constructor and handle method. It includes request validation and logging. ```php readonly class TrackEvent { public function __construct( public \Illuminate\Http\Request $request ) {} public function handle( MyOrg\Activity\Ledger\Events $events ) { $event = $this->request->validate([ 'name' => ['required', 'string'], ]); abort_unless($events->log($event['name'], $request->getUserIp())); // ... } } ``` -------------------------------- ### Install Craft 6 Upgrade Tool Source: https://craftcms.com/docs/6.x/upgrade Install the global composer package for the Craft 6 upgrade tool. This tool helps examine your project structure and identify potential issues before upgrading. ```bash composer global install craftcms/craft6-revamp ``` -------------------------------- ### Run Craft 6 Upgrade Tool in Docker Source: https://craftcms.com/docs/6.x/upgrade If you cannot install composer packages directly on your host, you can use a Docker container to run the upgrade tool. Mount your project directory into the container and execute the commands within it. ```bash # On the host machine... docker run --interactive --tty --volume $PWD:/app composer bash # ...in the container: -> 42dae745a6ab:/app# composer global require craftcms/craft6-revamp -> ... -> 42dae745a6ab:/app# composer global exec craft6-revamp ``` -------------------------------- ### Accessing Request Data in Twig Source: https://craftcms.com/docs/6.x/upgrade Use `app('request').get('paramName')` to retrieve data from GET query strings or POST bodies, replacing `craft.app.request.getQueryParam()`. ```twig {% set queryParam = app('request').get('paramName') %} ``` -------------------------------- ### Run Craft Upgrades with DDEV Source: https://craftcms.com/docs/6.x/upgrade Use the `craft up` command via DDEV to initiate the upgrade process. Ignore warnings about the application being in production, as they are safe to dismiss. ```bash ddev php craft up ``` -------------------------------- ### Using Facade with Dependency Injection Source: https://craftcms.com/docs/6.x/extend/services Shows how a previously defined facade can be used to call methods on the wrapped service, benefiting from dependency injection. ```php Reports::fork($template, $currentUser); ``` -------------------------------- ### Configuring Custom Rate Limiter Source: https://craftcms.com/docs/6.x/extend/http Demonstrates how to define a custom rate limiter in the plugin's boot method, using settings to configure the limit per minute based on the client's IP address. ```php public function bootPlugin() { RateLimiter::for('activity', function (Request $request) { $limit = $this->getSettings()->maxEventsPerMinute; return Limit::perMinute($limit)->by($request->getClientIp()); }); } ``` -------------------------------- ### Subscribe to Events Source: https://craftcms.com/docs/6.x/extend/events Bundle multiple event handlers into a single class using subscribers. Register your subscribers in the `bootPlugin()` method. ```php Event::subscribe(CustomerRetentionSubscriber::class); ``` -------------------------------- ### Accessing Craft Services via app() Container Source: https://craftcms.com/docs/6.x/extend/services Demonstrates accessing a core Craft CMS service, ProjectConfig, using the global `app()` helper function when not in a DI-capable context. ```php app("CraftCms\Cms\ProjectConfig\ProjectConfig")->get('graphql.enabled'); ``` -------------------------------- ### Create Project Directory Source: https://craftcms.com/docs/6.x/install Create a new directory for your Craft CMS project and navigate into it. ```bash mkdir my-craft-project cd my-craft-project/ ``` -------------------------------- ### Define Custom Filesystem Class Source: https://craftcms.com/docs/6.x/extend/disks Extend `CraftCms\Cms\Filesystem\Filesystems\Filesystem` and implement `getDiskConfig()` to return disk configuration settings. Craft populates the filesystem instance with settings from the control panel. ```php use CraftCms\Cms\Filesystem\Filesystems\Filesystem; use CraftCms\Cms\Support\Env; class Backblaze extends Filesystem { // ... public function getDiskConfig(): array { return [ 'driver' => 'b2', 'bucketId' => Env::parse($this->bucketId), 'bucketName' => Env::parse($this->bucketName), 'accountId' => Env::parse($this->accountId), 'applicationKey' => Env::parse($this->applicationKey), 'url' => Env::parse($this->url), // ... ] } } ``` -------------------------------- ### Basic Vite Configuration (vite.config.js) Source: https://craftcms.com/docs/6.x/extend/assets Set up the Vite configuration file to match your plugin's asset inputs and public directory. This ensures Vite processes and serves assets correctly. ```javascript import { defineConfig } from 'vite'; import laravel from 'laravel-vite-plugin'; export default defineConfig({ plugins: [ laravel({ input: [ 'resources/js/plugin.js', 'resources/css/plugin.css' ], publicDirectory: 'resources/dist', }), ], }); ``` -------------------------------- ### Accessing Services in Craft 2.x and 3.x-5.x Source: https://craftcms.com/docs/6.x/extend/services Illustrates the historical methods for accessing services in older Craft CMS versions. ```php # Craft 2.x: craft()->activityPlugin_reportsService->generate(...); # Craft 3.x–5.x: Activity::getInstance()->getReports()->generate(...); ``` -------------------------------- ### Register Callback for App Booted Event Source: https://craftcms.com/docs/6.x/extend/avenues Register a callback to be notified when the application has fully booted. This ensures that all services and plugins are ready. ```php app()->booted($callback); ``` -------------------------------- ### List All Routes with Middleware Source: https://craftcms.com/docs/6.x/extend/http Use the `route:list` Artisan command to view all registered routes. The `-v` flag displays middleware groups, and `-vv` lists every middleware class applied to each route. ```bash ddev artisan route:list --path=some-path-segment -v ``` -------------------------------- ### Register Custom Filesystem Type Source: https://craftcms.com/docs/6.x/extend/disks Listen for the `RegisterFilesystemTypes` event to register your custom filesystem class. Ensure any non-standard Flysystem adapters are added to your `composer.json`. ```php use CraftCms\Cms\Filesystem\Events\RegisterFilesystemTypes; use Illuminate\Support\Facades\Event; Event::listen(function (RegisterFilesystemTypes $event) { $event->types->push(Backblaze::class); }); ``` -------------------------------- ### Define Control Panel Routes (cp.php) Source: https://craftcms.com/docs/6.x/extend/http Use this file for control panel specific routes. These routes are prefixed with your `cpTrigger` and include additional `craft.cp` middleware for permissions. ```php # cp.php // Example routes would go here, prefixed with your cpTrigger ``` -------------------------------- ### Define Front-End Routes (web.php) Source: https://craftcms.com/docs/6.x/extend/http Use this file to define standard front-end routes for your plugin. Routes are registered with `craft` and `craft.web` middleware. ```php use MyOrg\Activity\Http\Controllers\TrackEvent; use Illuminate\Support\Facades\Route; Route::post('activity/track', TrackEvent::class); ``` -------------------------------- ### Defining a Singleton Service in Craft 6.x Source: https://craftcms.com/docs/6.x/extend/services Shows how to define a service class marked with the Singleton attribute for instance reuse via Laravel's service container. ```php namespace MyOrg\Activity\Reporting; #["\Illuminate\Container\Attributes\Singleton"] class Manager { public function generate(Template $template): Report { // ... } } ``` -------------------------------- ### Dependency Injection in Controller Constructor Source: https://craftcms.com/docs/6.x/extend/services Illustrates how to use dependency injection in a controller's constructor to automatically receive required services. ```php namespace MyOrg\Activity\Http\Controllers; use Symfony\Component\HttpFoundation\Response; readonly class TrackEvent { public function __construct( public \Illuminate\Http\Request $request ) {} public function handle( MyOrg\Activity\Ledger\Events $events ): Response { if (! $events->track($this->validated('name'))) { abort(400, 'The event could not be tracked.'); } return new JsonResponse(['success' => true]); } } ``` -------------------------------- ### Create Scoped Disk with Subpath Source: https://craftcms.com/docs/6.x/extend/disks Return a `prefix` config key from `getDiskConfig()` to create a scoped disk. This quarantines operations to a non-root directory within the configured filesystem. ```php return [ // ... 'prefix' => 'my-scoped-directory', ]; ``` -------------------------------- ### Configure Vite Assets in Plugin (PHP) Source: https://craftcms.com/docs/6.x/extend/assets Define Vite entry points and directories within your plugin's PHP class. This registers assets for publishing and enables hot file discovery. ```php class MyPlugin extends \CraftCms\Cms\Plugin\Plugin { protected array $vite = [ 'input' => [ 'resources/js/plugin.js', 'resources/css/plugin.css', ], 'publicDirectory' => 'resources/dist', 'buildDirectory' => 'build', ]; } ``` -------------------------------- ### Run Craft Migrations Source: https://craftcms.com/docs/6.x/upgrade After running the upgrade tool, execute the Craft migrations to apply necessary changes to your database schema. This command handles tasks like replacing legacy class names with new namespaces. ```bash # Use the new `artisan` entrypoint... ddev artisan craft:up ``` -------------------------------- ### Scaffold Craft CMS Project Source: https://craftcms.com/docs/6.x/install Use Composer to create a new Craft CMS project from the official starter template for version 6.x. ```bash ddev composer create-project "craftcms/craft:^6.0.0-alpha.1" ``` -------------------------------- ### Publish Laravel Configuration Files Source: https://craftcms.com/docs/6.x/upgrade Publish specific Laravel configuration files from the console to customize settings like authentication, session, and mailers. This is useful for adapting to new configuration structures. ```bash ddev artisan config:publish [auth|session|hashing|mail|...] ``` -------------------------------- ### Emit an Event Using the `event()` Helper Source: https://craftcms.com/docs/6.x/extend/events Use the `event()` helper function to emit an event instance. Store the event instance in a variable if you need to check its outcome or properties later. ```php event($confirmation = new DeliveryConfirmed); ``` -------------------------------- ### Bind Event Listener Using a Closure Source: https://craftcms.com/docs/6.x/extend/events Event listeners can be defined as closures and must be bound within the plugin's `bootPlugin()` method. This allows for dynamic listener registration. ```php use CraftCms\Cms\Filesystem\Events\RegisterFilesystemTypes; use MyVendor\MyPlugin\Filesystems\Dropbox // ... public function bootPlugin(): void { // Closures must be bound in your plugin’s bootPlugin() method, as PHP doesn’t support them as static class properties! Event::listen(function(RegisterFilesystemTypes $event): void { $event->types->push(Dropbox::class) }); } ``` -------------------------------- ### Flashing Messages with Redirects Source: https://craftcms.com/docs/6.x/extend/session In a controller, use this pattern to flash messages to the session when redirecting. The `with()` method can be used to add single messages or an array of data. Ensure data is serializable. ```php return back() ->with('error', t('This is already on your favorites list!')) ->with([ 'favorite' => CraftCms\Cms\Support\Arr::toArray($favorite), ]); ``` -------------------------------- ### Publishing Plugin Assets with Artisan Source: https://craftcms.com/docs/6.x/extend/assets Use the `vendor:publish` Artisan command to publish plugin assets, especially after making changes to files in the `resources/` directory. This command ensures assets are copied to the correct location. ```bash ddev artisan vendor:publish --tag=your-plugin-handle ``` -------------------------------- ### Test Mailer Configuration Source: https://craftcms.com/docs/6.x/upgrade Test your mailer configuration using the `craft:mailer:test` Artisan command. This helps verify that email sending is correctly set up after migrating to Laravel's mailer. ```bash ddev artisan craft:mailer:test ``` -------------------------------- ### Define a Command Closure in Craft CMS Source: https://craftcms.com/docs/6.x/extend/commands Create simple commands directly within your plugin's bootPlugin() method using Artisan::command. This is suitable for straightforward, single-purpose commands. ```php Artisan::command('report:generate {templateId}', function (Manager $manager, int $templateId) { $template = $manager->getTemplateById($templateId); $this->info("Queueing report: {$template->name}!"); // ... }); ``` -------------------------------- ### Applying Rate Limiter Middleware to Routes Source: https://craftcms.com/docs/6.x/extend/http Shows how to apply the custom 'activity' rate limiter middleware to a specific route group in `routes/web.php`. ```php Route::middleware(['throttle:activity'])->group(function () { Route::post('activity/track', TrackEvent::class); }); ``` -------------------------------- ### Accessing Services via Service Container in Craft 6.x Source: https://craftcms.com/docs/6.x/extend/services Demonstrates direct access to a service class using its fully qualified name through the application's service container. ```php app("MyOrg\Activity\Reporting\Manager::class")->generate(...); ``` -------------------------------- ### Configure DDEV for Craft CMS 6.x Source: https://craftcms.com/docs/6.x/install Configure DDEV for a new Craft CMS 6.x project, specifying the project type, document root, PHP version, and database. ```bash # Note the new project type for 6.x! ddev config --project-type=laravel --docroot=public --php-version=8.5 --database=mysql:8.4 ``` -------------------------------- ### Dispatch Jobs Using the `dispatch()` Helper in PHP Source: https://craftcms.com/docs/6.x/extend/queue Jobs can be dispatched using the global `dispatch()` helper or by calling the static `dispatch()` method on the job class itself, which forwards arguments to the constructor. ```php dispatch(new GenerateReport(1234, true)); ``` ```php GenerateReport::dispatch(1234, true); ``` -------------------------------- ### Define a Custom Event Class Source: https://craftcms.com/docs/6.x/extend/events Create a new class for your event. If relevant data needs to be passed, attach it as a property to the event class. ```php namespace MyOrg\Store\Shipping\Events; class DeliveryConfirmed {} ``` -------------------------------- ### Define Action Path Routes (actions.php) Source: https://craftcms.com/docs/6.x/extend/http This file provides compatibility with Yii's action path routing scheme. Routes defined here are registered twice, with different prefixes and middleware. ```php # routes/actions.php Route::prefix('activity', function() { Route::post('events/log', TrackEvent::class); }); # Two routes: # -> actions/activity/events/log # -> admin/actions/activity/events/log ``` -------------------------------- ### Register Command Classes in Plugin Source: https://craftcms.com/docs/6.x/extend/commands Add the fully qualified class name of your command to the $commands array in your plugin's main class to register it. ```php public array $commands = [ MyOrg\MyPlugin\Reporting\Commands\GenerateReport::class, ]; ``` -------------------------------- ### Perform Cleanup After Response Source: https://craftcms.com/docs/6.x/extend/http For generalized termination callbacks, use `app()->terminating()` to perform cleanup after a response is sent, as Laravel's terminable middleware may not be compatible with all hosting environments. ```php app()->terminating($this->flushLazyEvents()); ``` -------------------------------- ### Vite Server Configuration for DDEV (JS) Source: https://craftcms.com/docs/6.x/extend/assets Customize Vite's server configuration for development environments like DDEV, including host, port, and CORS settings. Ensure unique ports and hot files if managing multiple plugins. ```javascript // ... const host = '0.0.0.0'; const port = 3000; const origin = `${process.env.DDEV_PRIMARY_URL}:${port}`; export default defineConfig({ server: { host, origin, port, cors: { // Live dangerously... origin: true, // ...or permit only DDEV origins. // See: https://dev.to/mandrasch/vite-is-suddenly-not-working-anymore-due-to-cors-error-ddev-3673 // origin: /https?:\/([A-Za-z0-9\-]+\.)?(\.ddev.site)(?::\d+)?$/, }, }, // ... }); ``` -------------------------------- ### Custom Eval Command Implementation Source: https://craftcms.com/docs/6.x/upgrade Re-implement the removed `exec` command by creating a closure command in a service provider to evaluate arbitrary PHP code. ```php public function boot() { Artisan::command('eval {code}', function ($code) { $output = ''; $this->components->task( 'Evaluating PHP code', function () use ($code, &$output) { ob_start(); eval($code); $output = ob_get_clean(); } ); $this->line($output); return 0; }); } ``` -------------------------------- ### Define a Command Class in Craft CMS Source: https://craftcms.com/docs/6.x/extend/commands Use this class structure to define a command that extends Illuminate\Console\Command. Arguments and options are defined via attributes or a protected $signature property. The handle method contains the command's logic. ```php namespace MyOrg\MyPlugin\Reporting\Commands; use MyOrg\MyPlugin\Reporting\Manager; use CraftCms\Cms\Console\CraftCommand; use Illuminate\Console\Attributes\Description; use Illuminate\Console\Attributes\Signature; use Illuminate\Console\Command; #[Signature('report:generate {templateId}')] #[Description('Start building a report')] class GenerateReport extends Command { use CraftCommand; // Alternatively, the signature can be set via a property: // protected $signature = 'report:generate {teamId}'; public function handle(Manager $manager, int $templateId): void { $template = $manager->getTemplateById($templateId); $this->info("Queueing report: {$template->name}!"); // ... } } ``` -------------------------------- ### Register a System Message Source: https://craftcms.com/docs/6.x/extend/mail Register a new system message with the `RegisterSystemMessages` event in your plugin's `bootPlugin()` method. This defines a custom email message with a key, heading, subject, and body. ```php use Illuminate\Support\Facades\Event; use CraftCms\Cms\SystemMessage\Data\SystemMessage; use CraftCms\Cms\SystemMessage\Events\RegisterSystemMessages; Event::listen(function(RegisterSystemMessages $event) { $event->messages->push(new SystemMessage( key: 'report_finished', heading: 'When a report is finished generating', subject: 'Here is your {report.template.name} report', body: "Hi, {report.creator.fullName}!\n\nA {report.template.name} just finished running. To download it, ...", )); }); ``` -------------------------------- ### Define a Macro for Site Component Source: https://craftcms.com/docs/6.x/extend/macros Define a custom macro `isB2b` on the `Site` component within your plugin's `bootPlugin()` method. This macro checks if the site handle contains 'biz'. ```php use CraftCms\Cms\Site\Data\Site; public function bootPlugin() { Site::macro('isB2b', function() { return str_contains($this->handle, 'biz'); }); } ``` -------------------------------- ### Guard Route with Permission Source: https://craftcms.com/docs/6.x/extend/http Apply a known permission to a route using the `can` middleware. This ensures that only users with the specified permission can access the route. ```php Route::get('events/history', ListHistory::class) ->middleware(['can:eventsPlugin-viewEventHistory']); ``` -------------------------------- ### Composer.json Configuration for Plugin Providers Source: https://craftcms.com/docs/6.x/extend/provider Add this entry to your plugin's `composer.json` file to register its root service provider with Laravel. ```json { // ... "extra": { "handle": "_demo-plugin", "name": "Demo Plugin", "developer": "Pixel & Tonic", "documentationUrl": "", "class": "CraftCms\\DemoPlugin\\Demo", "laravel": { "providers": [ "CraftCms\\DemoPlugin\\Demo" ] } } } ``` -------------------------------- ### Send a System Message Source: https://craftcms.com/docs/6.x/extend/mail Send a registered system message by first creating the mailable object using the `SystemMessages` service and then passing it to Laravel's `Mail` facade for sending. ```php use CraftCms\Cms\SystemMessage\SystemMessages; use Illuminate\Support\Facades\Mail; $message = app(SystemMessages::class)->mailable('report_finished', $report->creator, [ 'report' => $report, ]); Mail::send($message); ``` -------------------------------- ### Dispatch an Event Statically Source: https://craftcms.com/docs/6.x/extend/events Alternatively, events can be dispatched using a static method, passing any necessary arguments. ```php DeliveryConfirmed::dispatch($args); ``` -------------------------------- ### Log Deprecation Notices with Deprecator Facade Source: https://craftcms.com/docs/6.x/extend/logging Use this method to send deprecation notices to the Deprecation Warnings utility. This is useful for logging warnings for newly-deprecated APIs throughout 6.x or within a compatibility layer. ```php CraftCms\Cms\Support\Facades\Deprecator::log(__METHOD__, 'The track() Twig function is deprecated. Activity plugin APIs have been consolidated into the global `activity` variable: {% do activity.track(...) %}'); ``` -------------------------------- ### Registering Custom Asset Bundle in Craft CMS Source: https://craftcms.com/docs/6.x/extend/assets Define a custom asset bundle by implementing `LegacyAssetInterface` and registering it with the `InternalAssetRegistry`. This is useful for managing dependencies and registering JS/CSS files. ```php namespace MyOrg\MyPlugin\Assets; use MyOrg\MyPlugin\Plugin; use CraftCms\Cms\View\LegacyAssets\LegacyAssetInterface; use CraftCms\Cms\View\HtmlStack; class ActivityGraph implements LegacyAssetInterface { // Optional: public array $depends = [ \CraftCms\Cms\View\LegacyAssets\D3Asset::class, ]; // Assets defined `$css` and `$js` should be directly registered against the passed HtmlStack: public function register(HtmlStack $htmlStack): void { $plugin = Plugin::getInstance(); $publicPath = public_path("vendor/{$plugin->packageName}"); $htmlStack->jsFile($publicPath.'/build/js/activity.js')); $htmlStack->cssFile($publicPath.'/build/css/activity.css')); } } ``` ```php use MyOrg\MyPlugin\Assets\ActivityGraph; use CraftCms\Cms\View\LegacyAssets\InternalAssetRegistry; app(InternalAssetRegistry::class)->register(ActivityGraph::class); ``` -------------------------------- ### Plugin Settings Validation Source: https://craftcms.com/docs/6.x/extend/config Use `getRules()` to define validation rules for plugin settings. This method should merge parent rules with custom rules for your settings. ```php // Remove: public function defineRules(): array {} // Add... public function getRules(): array { return array_merge(parent::getRules(), [ 'adminNotificationAddress' => ['required', 'email:rfc'], ]); } // ...or: public function rulesClass(): string { return MyPluginSettingsRuleset::class; } ``` -------------------------------- ### Log Deprecation Before Calling New API Source: https://craftcms.com/docs/6.x/extend/adapter When refactoring or splitting classes, keep legacy versions and log deprecation warnings before calling the new API. This helps users transition smoothly. ```php namespace myorg\pluginname\services; use MyOrg\PluginName\Shipping\Actions\ConfirmSignature; use MyOrg\PluginName\Models\Delivery; class Shipping { public function confirmSignature(Delivery $delivery, Asset $proof): void { \CraftCms\Cms\Support\Facades\Deprecator::log(__METHOD__, 'myorg\pluginname\services\Shipping::confirmSignature() is deprecated. Dispatch the MyOrg\PluginName\Shipping\Actions\ConfirmSignature action, instead.'); // Supposing we implemented this as a job or event ConfirmSignature::dispatch($delivery, $proof); } } ``` -------------------------------- ### PHP Markdown Facade Usage Source: https://craftcms.com/docs/6.x/upgrade When parsing Markdown in PHP, such as in Element API transformers, use the `Markdown` facade. This replaces the previous Yii Markdown engine. ```php use CraftCms\Cms\Support\Facades\Markdown; Markdown::parse($text); Markdown::parseParagraph($line); ``` -------------------------------- ### Register Callback for App Terminating Event Source: https://craftcms.com/docs/6.x/extend/avenues Register a callback to execute cleanup tasks at the end of the application's lifecycle. This is similar to Yii's EVENT_AFTER_REQUEST. ```php app()->terminating($callback); ``` -------------------------------- ### Alias Compatible Classes in Old Namespace Source: https://craftcms.com/docs/6.x/extend/adapter Use `class_alias` to make old class names point to new implementations. This is useful for classes that are completely compatible and don't require significant changes. ```php namespace myorg\pluginname\base; // (Optional) Keep original class definition inside conditional so it can still be picked up by IDEs: if (false) { /** * @deprecated 2.0.0 {@see \MyOrg\PluginName\Contracts\SomeInterface} */ interface SomeInterface {} } // Declare alias to new class: class_alias(\MyOrg\PluginName\Contracts\SomeInterface::class, SomeInterface::class)) ``` -------------------------------- ### Declaring Publishable Scripts and Styles in Craft CMS Plugin Source: https://craftcms.com/docs/6.x/extend/assets Declare publishable scripts and styles for control panel requests using the `$scripts` and `$styles` properties in your plugin class. These assets will be copied to the public vendor directory. ```php class Activity extends Plugin { // ... public array $scripts = [ __DIR__.'/../resources/css/sparkline.css' => 'css/sparkline.css', ]; public array $styles = __DIR__.'/../resources/js/sparkline.js' => 'js/sparkline.js', ]; // ... } ``` -------------------------------- ### Define a Custom Job Class in PHP Source: https://craftcms.com/docs/6.x/extend/queue Extend the base `CraftCms\Cms\Queue\Job` class to create custom jobs. Laravel can automatically inject dependencies into the `handle` method. ```php namespace MyOrg\Activity\Reporting\Jobs; use CraftCms\Cms\Queue\Job; use MyOrg\Activity\Reporting\Manager; class GenerateReport extends Job { public function __construct( public int $reportId, public bool $notifyOwner, ) {} // Laravel can automatically inject dependencies when it runs your job: public function handle( Manager $manager, ): void { $report = $manager->getReportById($this->reportId); } } ``` -------------------------------- ### Deprecate Service Getters and Use Service Container Source: https://craftcms.com/docs/6.x/extend/adapter When a service getter is deprecated, log the deprecation and instruct users to obtain the new service instance from the Laravel service container. ```php namespace myorg\myplugin; use CraftCms\Cms\Support\Facades\Deprecator; class Analytics extends \MyOrg\MyPlugin\Analytics { // ... public function getReports(): Reports { Deprecator::log(__METHOD__, 'myorg\myplugin\Analytics::getReports() is deprecated. Get an instance of MyOrg\MyPlugin\Analytics\Reports\Manager using the Laravel service container, instead.'); return app(MyOrg\MyPlugin\Analytics\Reports\Manager::class); } } ``` -------------------------------- ### Declare Plugin Dashboard Widgets Source: https://craftcms.com/docs/6.x/extend/avenues Use the `HasWidgets` trait to declare custom dashboard widgets for your plugin. Add a `$widgets` property to your main plugin class, listing the widget classes. ```php use CraftCms\Cms\Plugin\Plugin; use MyOrg\MyPlugin\Widgets\RandomQuoteWidget; class InspirationPlugin extends Plugin { // ... protected array $widgets = [ RandomQuoteWidget::class, ]; } ``` -------------------------------- ### Displaying General Flash Messages in Twig Source: https://craftcms.com/docs/6.x/extend/session This Twig snippet retrieves and displays general flash messages (success, notice, error) from the session. It iterates through the available flashes and renders them with appropriate CSS classes. ```twig {% set flashes = session().only(['success', 'notice', 'error']) %} {% if flashes is not empty %} {% endif %} ``` -------------------------------- ### Bind Listeners to Events in a Plugin Source: https://craftcms.com/docs/6.x/extend/events Define event listeners within your plugin's `$events` property. This array maps event classes to their corresponding listeners, which can be classes or callables. ```php use CraftCms\Cms\Cp\Events\DefineElementCardHtml; use CraftCms\Cms\Element\Events\DraftApplied; use CraftCms\Cms\Plugin\Plugin; use Illuminate\Mail\Events\MessageSending; class DemoPlugin extends Plugin { // ... // Each item in this array should have an event class as its key, and one or more listeners as values: protected array $events = [ // Craft event: DraftApplied::class => Listeners\LogElementActivity::class, // Laravel event: MessageSending::class => Listeners\LogMailMessage::class, // Multiple listeners can be attached to a single event name: DefineElementCardHtml::class => [ // Listener class: Listeners\AddCardDetails::class, // “Callable” array: [self::class, 'redactSensitiveCardData'], ], ]; // ... } ``` -------------------------------- ### Use a Custom Macro in Twig Source: https://craftcms.com/docs/6.x/extend/macros Call the custom `isB2b` macro on a `Site` object within a Twig template to conditionally display content. ```twig {{ currentSite.isB2b() ? 'Welcome, Mr. Business!' : 'Hey, bud!' }} ``` -------------------------------- ### Twig Error List Macro Source: https://craftcms.com/docs/6.x/upgrade This macro displays a list of errors for a given field, useful for form validation feedback. It checks if errors exist for the field before rendering the list. ```twig {% macro errorList(errors, field) %} {% if errors.has(field) ?? false %} {% endif %} {% endmacro %} ``` -------------------------------- ### Using the `old()` Function for Input Values Source: https://craftcms.com/docs/6.x/extend/session The `old()` function in Twig retrieves previously submitted input values, often used in conjunction with validation errors to repopulate form fields. Be cautious of XSS vulnerabilities when displaying user-submitted data. ```twig {{ input('text', 'name', old('title')) }} ``` -------------------------------- ### Displaying Validation Errors in Twig Source: https://craftcms.com/docs/6.x/extend/session Use this Twig code to check for and display validation errors associated with a specific field, such as 'title'. Errors are stored in the `errors` ViewErrorBag. ```twig {% if errors.has('title') %} {% endif %} ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.