Foundation framework upgrade (Laravel 6 LTS)

Release Note 11

Due to overwhelming support from the community, October CMS is updating its foundation framework to the latest Long Term Support (LTS) release. As a result, there are some new requirements to run October and some code changes required.

This guide is intented to help understand the changes associated from upgrade to Laravel 6 as the foundation framework used by October CMS.

Affected functionality

If you are using any of the following functionality it's highly recommended that you take a look at the relevant section in this guide and make any required changes to your usage:

Configuration Files (config/* files)

Some new configuration files have been made available as part of the Laravel 6 upgrade. You should check these configuration files to your project's config folder, and adjust the configuration as necessary.

Environment Variables (.env files)

If you are using .env files with a variable that has a # inside of an unquoted value; that will now be treated as a comment due to an upgrade to the phpdotenv library used for parsing them.

If you have any # characters inside of unquoted environment variables please update them to be quoted instead (ex. DB_PASS=23das#sdfas must be updated to DB_PASS="23das#sdfas").

Additionally, putenv() no longer changes the value returned by calls to env() as the env helper is now considered read-only. If dynamically changing configuration is required, it is recommended to use Config values instead as they can be dynamically changed with Config::set()

Note: .env files now require any values with spaces in them to be quoted too, it's recommended to just enclose every single value in .env with double quotes.

Using any Laravel Based Packages

The version of Laravel has been changed from 5.5 LTS to 6.x LTS. If you are using packages made for Laravel you may have to go through and update them to a version compatible with Laravel 6.x.

Interacting with Cache Repositories Directly

Cache TTL (time-to-live) values that are specified as an integer are treated as seconds now, as opposed to minutes, when interacting directly with a cache repository. If you interact with the cache directly, we recommend that you use DateTime or Carbon instances to define when your data is to expire (ex. now()->addMinutes(60) instead of 60). If you wish to continue using integers, you will need to multiply your integer values by 60 to get the number of seconds.

String-based Primary Keys in Models

If you are using string based primary keys for your models add protected $keyType = 'string'; to the model class definition to prevent performance optimizations meant for integer key types from negatively affecting your code.

Use of $guarded in Models

Due to a recent security patch made in the Laravel framework (see discussion), if a model uses the $guarded property to define attributes that are to be protected from mass assignment, then any attempts to use mass-assignment to populate a property / attribute of the model that does not exist in the database will fail. Example:

MyModel extends Model
{
    $guarded = ['id'];

    $someProperty = null;

    public function setSomePropertyAttribute($value)
    {
        $this->someProperty = $value
    }
}

Calling MyModel::create(['someProperty' => 'data']); would previously have worked, but will not anymore.

This specifically affected the October\Rain\Database\Attach\File model (and by extension, the System\Models\File model), which now use the "fillable" attributes property to define the fields available for mass assignment, as opposed to the "guarded" attributes property.

If you extend either of these models to provide your own custom File model and wish to have extra fields stored through mass assignment, you will need to copy the $fillable attribute from the October\Rain\Database\Attach\File model and place it in your own extension, adding any extra fields that you wish to be fillable as well.

Wildcard event listeners: Event::listen('example.*', $listener);

The parameters sent to wildcard event listeners in October has changed to match what Laravel has done since 5.4. This was overlooked in the 5.5 update but is being applied now. Going forward all wildcard event listeners will receive the name of the event currently being fired as the first parameter and an array of the event arguments as the second parameter.

Example of old wildcard listener:

Event::listen('*', function ($params) {
    if (Event::firing() === 'some.specific.event') {
        // do stuff with $params
    }
});

Example of new wildcard listener:

Event::listen('*', function ($event, $params) {
    if ($event === 'some.specific.event') {
        // do stuff with $params
    }
});

Catch-all routing

Changes to the routing in Laravel 6 have resulted in the behaviour of catch-all routes being changed in October CMS. Previously, a catch-all route could be defined with the following:

Route::any('{slug}', 'Backend\Classes\BackendController@run')->where('slug', '(.*)?');

The definition {slug} in Laravel 6 is now considered to be a required URL parameter, and will fail with an exception if the router sees an empty parameter. If your plugin uses a similar routing rule, and you would like URLs with an empty parameter to still be routed to your plugin, you must change this definition to be optional by suffixing the parameter name with the question mark (?) symbol. Eg:

Route::any('{slug?}', 'Backend\Classes\BackendController@run')->where('slug', '(.*)?');

This can be done immediately for your plugin routes, as optional parameters were already available in Laravel 5.5.

Using Carbon directly

The Carbon library has been upgraded from version 1 to version 2. While this should mostly work with existing code, please review the upgrade guide.

Using Jenssegers/Date directly

The jenssegers/date library has been removed from October entirely as most of its functionality is now present in Carbon 2.0. This should mostly work with existing code unless you were referencing it directly, in which case replace any references to Jenssegers\Date\Date with October\Rain\Argon\Date.

Using Symfony directly

Symfony has been upgraded to version 4 (except for the Yaml subsystem). If interacting directly with it, please review the upgrade guide

Using League CSV directly

The CSV package provided by The PHP League has been upgraded from version 8 to version 9. We have made the necessary adjustments to October CMS in order to accommodate this change, however, if you use the library directly or have extended the ImportModel and ExportModel classes, it is strongly recommended that you review the upgrade guide as several methods have been dropped or moved.

Unit Testing

If your plugin contains unit tests, you will need to make some adjustments to your unit tests in order to function with the Laravel 6 upgrade.

  • All setUp and tearDown methods in your unit test classes must now have the return type void specified to match PHPUnit 8's requirements.
    • Change all public function setUp() methods to public function setUp(): void.
    • Change all public function tearDown() methods to public function tearDown(): void.
  • The syntaxCheck attribute for the <phpunit> tag in your phpunit.xml file is now deprecated. This should be removed from your phpunit.xml file.

In addition to the changes above, note the following deprecations in PHPUnit 8 - these will be thrown as warnings in your unit tests.

  • The @expectedException group of docblock annotations are now deprecated. Instead, you should call the following methods inside your unit test method, depending on the annotation:
    • @expectedException: $this->expectException(ClassName::class)
    • @expectedExceptionCode: $this->expectExceptionCode(int)
    • @expectedExceptionMessage: $this->expectExceptionMessage(string)
  • The assertInternalType() method is no longer available. Instead, you should use the arrayIs[Type]() methods, where Type is the PHP variable type with a capital letter. For example, instead of assertInternalType('string', ...), you should use assertIsString, or assertInternalType('array', ...) should be assertIsArray.
  • The assertContains() method no longer tests whether strings are inside strings - it now will only detect if an item is contained in an array. If you are using assertContains() in this fashion, you should instead use assertStringContainsString().

For more information on deprecations, see the PHPUnit 8 release notes. Note that although the assertArraySubset() method was deprecated, we are still maintaining the method through requiring the dms/phpunit-arraysubset-asserts library, so you may continue to use that assertion.

Further Reading

Laravel Upgrade Guides

Package Upgrade Guides

PHP Upgrade Guides

Migrating from PHP 7.0 to PHP 7.1

Migrating from PHP 7.1 to PHP 7.2

Migrating from PHP 7.2 to PHP 7.3

Migrating from PHP 7.3 to PHP 7.4

comments powered by Disqus