diff --git a/docs/en/04_Changelogs/4.0.0.md b/docs/en/04_Changelogs/4.0.0.md index 01cb57bc0..0cfc6c265 100644 --- a/docs/en/04_Changelogs/4.0.0.md +++ b/docs/en/04_Changelogs/4.0.0.md @@ -193,36 +193,40 @@ For example, if you have the below `_ss_environment.php` file, your `.env` would `_ss_environment.php`: - :::php - constructExtensions(); - } - } +```diff +-class MyClass extends Object +-{ +-} ++class MyClass ++{ ++ use Extensible; ++ use Injectable; ++ use Configurable; ++ ++ public function __construct() ++ { ++ // Only needed if using Extensible trait ++ $this->constructExtensions(); ++ } ++} +``` References to $this->class +```diff +-$obj->class ++get_class($obj); - :::php - // old - $obj->class - $this->class; - // new - get_class($obj); - static::class; - +-$this->class; ++static::class; +``` Upgrade parse_class_spec() - - :::php - // old - $spec = Object::parse_class_spec($spec); - // new - $spec = ClassInfo::parse_class_spec($spec); - +```diff +-$spec = Object::parse_class_spec($spec); ++$spec = ClassInfo::parse_class_spec($spec); +``` Upgrade create_from_string() - :::php - // old - $obj = Object::create_from_string('Varchar(100)'); - // new - $obj = Injector::inst()->create('Varchar(100)'); - +```diff +-$obj = Object::create_from_string('Varchar(100)'); ++$obj = Injector::inst()->create('Varchar(100)'); +``` Extensions +```diff - :::php - // old - Object::add_extension('File', 'Versioned'); - $has = Object::has_extension('File', 'Versioned'); - $extensions = Object::get_extensions('File'); - // new - File::add_extension(Versioned::class); - $has = File::has_extension(Versioned::class) - $extensions = File::get_extensions(); - - // new (alternate form) - // Note: The class the extension method is called on MUST be a parent class - // of the first argument. - DataObject::add_extension(File::class, Versioned::class); // alternate - $has = DataObject::has_extension(File::class, Versioned::class); // alternate - $extensions = DataObject::get_extensions(File::class); +-Object::add_extension('File', 'Versioned'); ++File::add_extension(Versioned::class); ++DataObject::add_extension(File::class, Versioned::class); // alternate + +-$has = Object::has_extension('File', 'Versioned'); ++$has = File::has_extension(Versioned::class); ++$has = DataObject::has_extension(File::class, Versioned::class); // alternate + +-$extensions = Object::get_extensions('File'); ++$extensions = File::get_extensions(); ++$extensions = DataObject::get_extensions(File::class); // alternate +``` #### Upgrade references to Session object Session object is no longer statically accessible via `Session::inst()`. Instead, Session is a member of the current request. -Before: - - :::php - public function httpSubmission($data, $form, $request) { - Session::set('loggedIn', null); - } - - -After: - - :::php - public function httpSubmission($data, $form, $request) { - $request->getSession()->set('loggedIn', null); - } +```diff +-public function httpSubmission($data, $form, $request) +-{ +- Session::set('loggedIn', null); +-} ++public function httpSubmission($data, $form, $request) ++{ ++ $request->getSession()->set('loggedIn', null); ++} +``` In some places it may still be necessary to access the session object where no request is available. @@ -422,8 +415,10 @@ You can resolve this in your model by adding an explicit cast to HTML for those Before: ```php -class MyObject extends ViewableData { - public function getSomeHTML { +class MyObject extends ViewableData +{ + public function getSomeHTML + { $title = Convert::raw2xml($this->Title); return "

{$title}

"; } @@ -462,38 +457,40 @@ streamlined simpler mechanism for uploading File dataobjects. A helper service `FileHandleField` is provided to assist with dependency injection. Where the asset-admin module is not installed this service will fall back to the `FileField` class instead. +Usages of UploadField will need to be upgraded as below. -Usages of UploadField will need to be upgraded as below: +Before: -3.x code - - - :::php - class MyClass extends DataObject { - public function getCMSFields() { - return new FieldList( - new UploadField('Files') - ); - } +```php +class MyClass extends DataObject +{ + public function getCMSFields() + { + return new FieldList( + new UploadField('Files') + ); } +} +``` -4.x code +After: +```php +use SilverStripe\ORM\DataObject; +use SilverStripe\Forms\FieldList; +use SilverStripe\Forms\FileHandleField; - :::php - use SilverStripe\ORM\DataObject; - use SilverStripe\Forms\FieldList; - use SilverStripe\Forms\FileHandleField; - - class MyClass extends DataObject { - public function getCMSFields() { - return FieldList::create( - Injector::inst()->create(FileHandleField::class, 'Files') - ); - } +class MyClass extends DataObject +{ + public function getCMSFields() + { + return FieldList::create( + Injector::inst()->create(FileHandleField::class, 'Files') + ); } - +} +``` #### Upgrade code that uses i18n @@ -521,25 +518,26 @@ The below demonstrates how you can provide new localisation strings for an objec including both plurals and cross-module localisations. - :::php - class MyObject extends DataObject, implements i18nEntityProvider +```php +class MyObject extends DataObject, implements i18nEntityProvider +{ + public function provideI18nEntities() { - public function provideI18nEntities() - { - return [ - 'MyObject.SINGULAR_NAME' => 'object', - 'MyObject.PLURAL_NAME' => 'objects', - 'MyObject.PLURALS' => [ - 'one' => 'An object', - 'other' => '{count} objects', - ], - 'AnotherSection.DESCRIPTION' => [ - 'default' => 'This is the description for this section', - 'module' => 'extendedmodule', - ], - ]; - } + return [ + 'MyObject.SINGULAR_NAME' => 'object', + 'MyObject.PLURAL_NAME' => 'objects', + 'MyObject.PLURALS' => [ + 'one' => 'An object', + 'other' => '{count} objects', + ], + 'AnotherSection.DESCRIPTION' => [ + 'default' => 'This is the description for this section', + 'module' => 'extendedmodule', + ], + ]; } +} +``` In YML format this will be expressed as the below: @@ -547,41 +545,43 @@ In YML format this will be expressed as the below: `mymodule/lang/en.yml`: - :::yaml - en: - MyObject: - SINGULAR_NAME: 'object' - PLURAL_NAME: 'objects' - PLURALS: - one: 'An object', - other: '{count} objects' - +```yaml +en: + MyObject: + SINGULAR_NAME: 'object' + PLURAL_NAME: 'objects' + PLURALS: + one: 'An object', + other: '{count} objects' +``` `extendedmodule/lang/en.yml`: - :::yaml - en: - AnotherSection: - DESCRIPTION: 'This is the description for this section' +```yaml +en: + AnotherSection: + DESCRIPTION: 'This is the description for this section' +``` Usage of these pluralised strings is through the existing _t() method, and require a `|` pipe-delimeter with a {count} argument. - :::php - public function pluralise($count) - { - return _t('MyObject.PLURALS', 'An object|{count} objects', [ 'count' => $count ]); - } +```php +public function pluralise($count) +{ + return _t('MyObject.PLURALS', 'An object|{count} objects', [ 'count' => $count ]); +} +``` In templates this can also be invoked as below: - :::ss - <%t MyObject.PLURALS 'An item|{count} items' count=$Count %> - +```ss +<%t MyObject.PLURALS 'An item|{count} items' count=$Count %> +``` #### Removed Member.DateFormat and Member.TimeFormat database settings @@ -609,7 +609,7 @@ In order to retain existing file paths in line with framework version 3 you shou Note that this will not allow you to utilise certain file versioning features in 4.0. -``` +```yaml SilverStripe\Filesystem\Flysystem\FlysystemAssetStore: legacy_paths: true ``` @@ -656,38 +656,29 @@ This is the subset of all image types which may be assigned to the `[api:Image]` manipulations applied to it. This should be used as the file type restriction on any `[api:UploadField]` which is intended to upload images for manipulation. -Before: - -```php -if($file instanceof Image) { - $upload = new UploadField(); - $upload->setAllowedFileCategories('image'); -} -``` - -After: - -```php -if ($file->getIsImage()) { - $upload = new UploadField(); - $upload->setAllowedFileCategories('image/supported'); -} +```diff +-if($file instanceof Image) { +- $upload = new UploadField(); +- $upload->setAllowedFileCategories('image'); +-} ++if ($file->getIsImage()) { ++ $upload = new UploadField(); ++ $upload->setAllowedFileCategories('image/supported'); ++} ``` In cases where image-only assets may be assigned to relationships then your datamodel should specify explicitly an `Image` datatype, or refer to `DBFile('image/supported')`. -E.g. - ```php class MyObject extends DataObject { - private static $has_one = array( + private static $has_one = [ "ImageObject" => "Image" - ); - private static $db = array( + ]; + private static $db = [ "ImageField" => "DBFile('image/supported')" - ); + ]; } ``` @@ -701,29 +692,22 @@ files to the asset persistence layer. As a wrapper for an individual file, you c methods to assign content from a local (e.g. temporary) file, a stream, or a string of content. You would need to upgrade your code as below. -Before: - - -```php -function importTempFile($tmp) { - copy($tmp, ASSETS_PATH . '/imported/' . basename($tmp)); - $file = new File(); - $file->setFilename('assets/imported/'.basename($tmp)); - $file->write(); -} -``` - -After: - -```php -public function importTempFile($tmp) -{ - Versioned::reading_stage('Stage'); - $file = new File(); - $file->setFromLocalFile($tmp, 'imported/' . basename($tmp)); - $file->write(); - $file->doPublish(); -} +```diff +-function importTempFile($tmp) +-{ +- copy($tmp, ASSETS_PATH . '/imported/' . basename($tmp)); +- $file = new File(); +- $file->setFilename('assets/imported/'.basename($tmp)); +- $file->write(); +-} ++public function importTempFile($tmp) ++{ ++ Versioned::reading_stage('Stage'); ++ $file = new File(); ++ $file->setFromLocalFile($tmp, 'imported/' . basename($tmp)); ++ $file->write(); ++ $file->doPublish(); ++} ``` Note that 'assets' is no longer present in the new code, and the path beneath what was once assets is now @@ -858,15 +842,14 @@ that handled saving of content into composite fields can be removed, as it is no The below describes the minimum amount of effort required to implement a composite DB field. ```php - 'Varchar(200)', 'Suburb' => 'Varchar(100)', 'City' => 'Varchar(100)', 'Country' => 'Varchar(100)' - ); + ]; public function scaffoldFormField($title = null) { @@ -958,9 +941,10 @@ type will raise a PHP error. Before: ```php -function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) { +function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) +{ $locale = Translatable::get_current_locale(); - if(!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhere()))) { + if(!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhere()))) { $qry = sprintf('"Locale" = \'%s\'', Convert::raw2sql($locale)); $query->addWhere($qry); } @@ -974,9 +958,9 @@ public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) { $locale = Translatable::get_current_locale(); if (!preg_match('/("|\'|`)Locale("|\'|`)/', implode(' ', $query->getWhereParameterised($parameters)))) { - $query->addWhere(array( + $query->addWhere([ '"Locale"' => $locale - )); + ]); } } ``` @@ -1085,9 +1069,9 @@ if it only has versioning without staging. */ class MyStagedModel extends DataObject { - private staic $extensions = array( + private staic $extensions = [ "SilverStripe\\ORM\\Versioning\\Versioned('StagedVersioned')" - ); + ]; } /** @@ -1095,9 +1079,9 @@ class MyStagedModel extends DataObject */ class MyVersionedModel extends DataObject { - private static $extensions = array( + private static $extensions = [ "SilverStripe\\ORM\\Versioning\\Versioned('Versioned')" - ); + ]; } ``` @@ -1147,18 +1131,20 @@ An exception to this is any classes which once had the `SS_` prefix, which will Before: -``` -class MyObject extends DataObject { - private static $db = array( +```php +class MyObject extends DataObject +{ + private static $db = [ 'Number' => 'Int', 'Time' => 'SS_Datetime' - ); + ]; /** * @param Int $val * @return Varchar */ - public function TextNumber() { + public function TextNumber() + { return new Varchar('TextNumber', 'Number is ' . $this->Number); } } @@ -1172,10 +1158,10 @@ use SilverStripe\ORM\FieldType\DBVarchar; class MyObject extends DataObject { - private static $db = array( + private static $db = [ 'Number' => 'Int', 'Time' => 'Datetime' - ); + ]; /** * @param Int $val @@ -1215,12 +1201,15 @@ specific functions. #### Upgrading custom Authenticators The methods `register` and `unregister` on `Authenticator` are deprecated in favor of the `Config` system. This means that any custom Authenticator needs to be registered through the yml config: + ```yaml SilverStripe\Security\Authenticator; authenticators: - MyVendor\MyModule\MyAuthenticator ``` + If there is no authenticator registered, `Authenticator` will try to fall back on the `default_authenticator`, which can be changed using the following config, replacing the MemberAuthenticator with your authenticator: + ```yaml SilverStripe\Security\Authenticator: default_authenticator: SilverStripe\Security\MemberAuthenticator @@ -1257,60 +1246,46 @@ This also allowed us to remove SilverStripe's `Cache` API and use dependency inj Caches should be retrieved through `Injector` instead of `Cache::factory()`, and have a slightly different API (e.g. `set()` instead of `save()`). -Before: +```diff +-$cache = Cache::factory('myCache'); ++use Psr\SimpleCache\CacheInterface; ++$cache = Injector::inst()->get(CacheInterface::class . '.myCache'); + +// create a new item by trying to get it from the cache +-$myValue = $cache->load('myCacheKey'); ++$myValue = $cache->get('myCacheKey'); - :::php - $cache = Cache::factory('myCache'); - - // create a new item by trying to get it from the cache - $myValue = $cache->load('myCacheKey'); - - // set a value and save it via the adapter - $cache->save(1234, 'myCacheKey'); - - // retrieve the cache item - if (!$cache->load('myCacheKey')) { - // ... item does not exists in the cache - } - - // Remove a cache key - $cache->remove('myCacheKey'); +// set a value and save it via the adapter +-$cache->save(1234, 'myCacheKey'); ++$cache->set('myCacheKey', 1234); +// retrieve the cache item +-if (!$cache->load('myCacheKey')) { +- // ... item does not exists in the cache +-} ++if (!$cache->has('myCacheKey')) { ++ // ... item does not exists in the cache ++} -After: - - - :::php - use Psr\SimpleCache\CacheInterface; - $cache = Injector::inst()->get(CacheInterface::class . '.myCache'); - - // create a new item by trying to get it from the cache - $myValue = $cache->get('myCacheKey'); - - // set a value and save it via the adapter - $cache->set('myCacheKey', 1234); - - // retrieve the cache item - if (!$cache->has('myCacheKey')) { - // ... item does not exists in the cache - } - - $cache->delete('myCacheKey'); - +// Remove a cache key +-$cache->remove('myCacheKey'); ++$cache->delete('myCacheKey'); +``` With the necessary minimal config in `_config/mycache.yml` - :::yml - --- - Name: mycache - --- - SilverStripe\Core\Injector\Injector: - Psr\SimpleCache\CacheInterface.myCache: - factory: SilverStripe\Core\Cache\CacheFactory - constructor: - namespace: 'mycache' +```yaml +--- +Name: mycache +--- +SilverStripe\Core\Injector\Injector: + Psr\SimpleCache\CacheInterface.myCache: + factory: SilverStripe\Core\Cache\CacheFactory + constructor: + namespace: 'mycache' +``` ##### Configuration Changes @@ -1320,36 +1295,37 @@ See our ["Caching" docs](/developer-guides/performance/caching) for more details Before (`mysite/_config.php`): - :::php - Cache::add_backend( - 'primary_memcached', - 'Memcached', - array( - 'servers' => array( - 'host' => 'localhost', - 'port' => 11211, - ) - ) - ); - Cache::pick_backend('primary_memcached', 'any', 10); +```php +Cache::add_backend( + 'primary_memcached', + 'Memcached', + [ + 'servers' => [ + 'host' => 'localhost', + 'port' => 11211, + ] + ] +); +Cache::pick_backend('primary_memcached', 'any', 10); +``` After (`mysite/_config/config.yml`): - :::yml - --- - After: - - '#corecache' - --- - SilverStripe\Core\Injector\Injector: - MemcachedClient: - class: 'Memcached' - calls: - - [ addServer, [ 'localhost', 11211 ] ] - SilverStripe\Core\Cache\CacheFactory: - class: 'SilverStripe\Core\Cache\MemcachedCacheFactory' - constructor: - client: '%$MemcachedClient - +```yaml +--- +After: + - '#corecache' +--- +SilverStripe\Core\Injector\Injector: + MemcachedClient: + class: 'Memcached' + calls: + - [ addServer, [ 'localhost', 11211 ] ] + SilverStripe\Core\Cache\CacheFactory: + class: 'SilverStripe\Core\Cache\MemcachedCacheFactory' + constructor: + client: '%$MemcachedClient +``` ## API Changes