See the Upgrade Guide for a quick highlight of any potentially required changes in your code in order to be compatible with Winter CMS v1.2+. Although the upgrade should be relatively painless it is still worth taking a quick look to verify that you are not affected by any of the relatively few breaking changes.
Due to the significant number of changes in 1.2, they are grouped by section rather than category for this release note.
Over the past year as we have been working on the Laravel 9 upgrade we have released a number of plugins, themes, and development tools designed to further enhance the Winter CMS developer experience.
The following first party plugins were released / revamped:
The following first party themes were released:
The official Winter CMS VSCode extension was released which provides code completion and syntax highlighting for Winter CMS projects.
A number of improvements have been made to the console commands available out of the box with Winter as well as the developer experience for working with the CLI in general. In addition to these improvements, the Console documentation has been updated and enhanced, click here to read the docs.
complete() function to the create:command Command scaffolding for providing autocompletion in console commands. (run artisan completion --help to setup autocompletion in your terminal)plugin:disable $nameplugin:enable $nameplugin:refresh $nameplugin:rollback $name $rollbackVersionwinter:passwd $username (last 20 updated backend users)migrate as an alias to winter:up to simplify transitioning to Winter from Laravelartisan --version will now include Winter CMS.route:list and route:cache now support module routes out of the box.key:generate command wouldn't set the APP_KEY value in the .env file if the .env file didn't exist yet.Winter\Storm\Console\Command base class that adds helpers for making it easier for commands to implement suggested values for autocompletion.alert() helper to the Winter Command base class that improves on the Laravel default by adding support for wrapping long alert messages to multiple lines.Winter\Storm\Console\Traits\ConfirmsWithInput trait provides the confirmWithInput($message, $requiredInput) method to require the user to input the required input string in order before proceeding with running the command.Winter\Storm\Console\Traits\ProcessesQuery trait that provides a processQuery($query, $callback, $chunkSize, $limit) method that simplifies the process of processing large numbers of records in console commands by handling creating and updating a progress bar, chunking the provided query by the provided chunk size and limit parameters, running the callback for each record, and gracefully handling any exceptions thrown during the processing of records.create:job Author.Plugin JobName scaffolding console command to create an initial Job classscaffold argument to create:theme command, defaults to less; also supports tailwind.Winter\Storm and into their relevant Modules (Backend, CMS, & System).$nameFrom property and getNameInput() method to the Winter\Storm\Scaffold\GeneratorCommand to enable checking for reserved names when generating code via scaffolding commands. Also made some method signature type hint changes to the class, be sure to review and apply them accordingly in any custom uses of that base class.getPluginIdentifier() method from the Winter\Storm\Scaffold\GeneratorCommand class and added a new base class for scaffolding resources that are specific to plugins: System\Console\BaseScaffoldCommand.plugin argument for scaffolders that extend System\Console\BaseScaffoldCommand.System\Console\Traits\HasPluginInput trait that provides helpers for when a console command interacts with plugin names as input arguments.getLangKeys() method defined on the scaffolding command during the execution process. This method can use the $this->vars property and $this->laravel->getLocale() method to return an associative array of message keys without the author prefix and their values for the current locale.plugin_id -> lowercase version of a plugin's identifierplugin_code -> Studly case version of the plugin's identifierplugin_url -> author/plugin, used in Backend::url() callsplugin_folder -> author/plugin, used when generating paths to files with the $ plugins directory path symbol or the ~ application path symbol.plugin_namespace -> Studly case version of the plugin's identifier in namespace form (i.e. Author\Plugin)mix:watch command to clean up after itself when terminated with SIGTERM.winter:version will now only normalize file contents before hashing if the file is a valid text-based file which improves the reliability of change detection on Windows.winter:version --changes would always display "We have detected the following modifications:" even when no modifications were detected.winter:passwd now returns the status codes instead of using exit($code)theme:sync it wasn't being detected by the theme:sync command.ThemeInstall, ThemeRemove, ThemeList, ThemeUse, & ThemeSync console commands have been moved from the System module to the CMS module.artisan key:generate and one will be generated and set for you..env file is now a first class citizen in Winter and configuration files refer to it by default. You can continue to run winter:env to generate a file pulling from your configuration to provide the default values.wintercms-twig instead of twig in .vscode/extensions.jsonserver.php is no longer needed in order for artisan serve to function; it can be removed.varcharmax as a configuration option for mysql database connections.app.tempPath configuration option to set the application's temporary path.winter:test command by first separating the arguments meant for Winter and the PHPUnit arguments / options with a --. Example: winter:test --module=system -- --filter=ImageResizerTesttests/ folder into individual tests/ folders under each core module. Modules can now be tested individually by running winter:test with the -m or --module options (ex. winter:test -m cms -m system -m backend)TestCase & PluginTestCase classes are now namespaced under the System module, extend System\Tests\Bootstrap\TestCase & System\Tests\Bootstrap\PluginTestCase instead.PluginManager::DISABLED_REQUEST flag. The following changes were made the System\Classes\PluginManager's public API:
bindContainerObjects(): No replacement.clearDisabledCache(): Use clearFlagCache() instead if required.registerReplacedPlugins(): Now handled as a part of loadPluginFlags().loadPluginFlags(): Loads the plugin flags (disabled & replacement states) from the cache regenerating them if required.clearFlagCache(): Resets the plugin flag cachegetPluginFlags(PluginBase|string $plugin): array: Retrieves any flags that are currently set for the provided plugin.freezePlugin(PluginBase|string $plugin): Flags the provided plugin as "frozen" (updates cannot be downloaded / installed).unfreezePlugin(PluginBase|string $plugin): "Unfreezes" the provided plugin, allowing for updates to be performed.checkDependencies() method to the PluginBase class to handle checking if the plugin's dependencies are present.plugin.yaml files by caching the parsed results of said files.System\Classes\VersionManager->getDatabaseHistory($pluginCode) from protected to public.v in their version identifiers would sometimes have issues when migrating between versions..php extension which reduces confusion about what templating language is used for backend template files. .htm files are still supported, but not recommended.ImageResizer class by only removing the resizer configuration from the Cache after the resizer has been successfully instantiated.ImageResizer::getDefaultDisk() method to get the default Storage disk used by the ImageResizer(ImageResizer)->getDisk() method to get the disk for the currently active imagegetMediaPath($path) and getStorageDisk() on System\Classes\MediaLibrary public (previously protected)getStorageDisk() and getMediaPath() methods on System\Classes\MediaLibrary public instead of protected.artisan create:theme mytheme tailwind@snowboard.base, @snowboard.request, @snowboard.attr, @snowboard.extras, & @snowboard.extras.css AssetCombiner aliases.Cms\Classes\Page isActive property wasn't being set if the URL was set to / and the currently requested URL was the home page.multiple: bool property on type: fileupload fields in the Theme Customization section to choose between an attachOne and attachMany relationship.App::make($environmentName);:
twig.environment The default System TwigEnvironment, is used by the Twig::parse() parsertwig.environment.mailer The Mailer TwigEnvironment, used by the MailManager class when rendering emailstwig.environment.cms The CMS TwigEnvironment, used by the CMS when rendering CMS templatesSystem\Classes\MarkupManager::makeBaseTwigEnvironment(TwigLoader $loader, array $options) static helper method that returns the basic TwigEnvironment that should be used by any custom Twig implementation in Winter CMS (i.e. applies the default security policy and SystemTwigExtension).transaction(), beginTransaction(), and endTransaction() methods from System\Classes\MarkupManager since "markup transactions" was a hacky workaround originally implemented due to global polution of the single Twig environment. This is no longer required with the use of multiple, separate twig environments.{% spaceless %} and {% filter %} Twig tokens to the core as Twig v3 removed them; it is recommended to avoid using them however.$twig->loadTemplate() has been changed, use $twig->load() instead.{% filter %} in the demo theme with {% apply %} instead.twig.environment.cms before it's used by the CMS in order to use or modify it.Twig::parse() before doing anything that rendered Mail content (i.e. sending an email) would cause the Mail twig rendering to stop working.CmsCompoundObject instances were not using the default CMS twig environment.Cms\Classes\Controller instance on the Cms\Twig\DebugExtension making it easier to reuse elsewhere.system.beforeRoute and system.route) that fire before and after the system route registration respectively and moved the registration of the Backend and CMS module routes to be after the system route registration. The CMS module's routes should now always be the last routes registered regardless of what happens.backend.layout.extendHead event now passes auth or default as the value for layout instead of auth.htm or default.htm.translator.beforeResolve event, use Lang::set($key, $value, $locale) instead.artisan create:model.add{$RelationType}Relation($name, $config) methods to the HasRelationships trait that make it easier to dynamically add new relationships to existing model classes.morphTo relation now uses the Winter\Storm\Database\MorphPivot class, similar to Laravel's own MorphPivot class. If you wish to use a custom pivot model for a morphTo relation, the pivot model must extend this class.Winter\Storm\Database\Traits\ArraySource trait that allows a model to be created, queried and managed from arbitrary data as opposed to a database table.getAdapter() method from Storage disk instancesgetPathPrefix() and setPathPrefix() methods to Storage disk instancesApp::before() to support plugin's overriding themModuleServiceProvider no longer needs a call to parent::register($module) in the register() method as route registration is now handled in the boot() method.Winter\Storm\Support\Facades\Str facade has been removed, use Winter\Storm\Support\Str directly instead.Symfony\Component\Debug\Exception\FatalErrorException has been removed, use Symfony\Component\ErrorHandler\Error\FatalError instead.db.schema instead of only resolved directly through the Schema facade.Winter\Storm\Parse\EnvFile parsing class for handling modifying the contents of environment (.env) files through PHP.Winter\Storm\Parse\PHP\ArrayFile parsing class for handling modifying the contents of Array Files (PHP config & localization files that return a single array and are used for storing data). The Winter\Storm\Config\ConfigWriter class has been rewritten to use the ArrayFile parser internally.Sign up to our newsletter and receive updates on Winter releases, new features in the works, plugin and theme promotions and much more!