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