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 $name
plugin:enable $name
plugin:refresh $name
plugin:rollback $name $rollbackVersion
winter: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.json
server.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=ImageResizerTest
tests/
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!