Merge pull request #7169 from open-sausages/pulls/4.0/upgrade-docs-need-an-upgrade

Upgrade the upgrading docs
This commit is contained in:
Daniel Hensby 2017-07-19 18:31:21 +01:00 committed by GitHub
commit 2bd24e0f98

View File

@ -10,6 +10,7 @@ guide developers in preparing existing 3.x code for compatibility with 4.0
* [Upgrading Guide](#upgrading)
* [Standard Upgrade](#upgrading-primary)
* [API Specific Upgrades](#upgrading-specifics)
* [User code style upgrades](#usercode-style-upgrades)
* [API Changes](#api-changes)
* [General and Core API](#overview-general)
* [ORM API](#overview-orm)
@ -18,7 +19,7 @@ guide developers in preparing existing 3.x code for compatibility with 4.0
* [i18n](#overview-i18n)
* [Email and Mailer](#overview-mailer)
* [SapphireTest](#overview-testing)
* [Commit History](#commit-history)
* [Security](#overview-security)
## <a name="overview"></a>Highlights of major changes
@ -26,10 +27,12 @@ guide developers in preparing existing 3.x code for compatibility with 4.0
is required.
* All code earlier marked as deprecated for 4.0 has now been removed. See
[deprecation documentation](/contributing/release_process) for information on code deprecation.
* All SilverStripe classes are now namespaced, and some have been renamed. This has major implications for
* All code has been migrated to follow the PSR-2 coding standard. Most significantly, all SilverStripe
classes are now namespaced, and some have been renamed. This has major implications for
arrangement of templates, as well as other references to classes via string literals or configuration.
Automatic upgrading tools have been developed to cope with the bulk of these changes (see
[upgrading notes](#upgrading)).
[upgrading notes](#upgrading)). Some classes have been rearranged so that each file contains only one class definition. It is recommended that user code follow the same convention.
For example, page types and their controllers should longer be collated in a single file.
* Object class has been removed.
* Asset storage has been abstracted, and a new concept of `DBFile` references via database column references
now exists in addition to references via the existing `File` dataobject. File security and protected files
@ -74,7 +77,14 @@ as a standard first point of upgrade.
#### Upgrade references to renamed and namespaced classes
Nearly all core PHP classes have been namespaced. For example, `DataObject` is now called `SilverStripe\ORM\DataObject`.
We have developed an [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) to (semi-)automatically update your 3.x code to the new naming. Here's an example how to upgrade your `mysite` folder:
The below tasks describe how to upgrade an existing site to remain compatible with the newly upgraded classes.
##### Using the upgrader tool to automatically upgrade
This task should be run on every upgraded project.
We have developed an [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) to (semi-)automatically
update your 3.x code to the new naming. Here's an example how to upgrade your `mysite` folder:
```
composer global require silverstripe/upgrader
@ -92,6 +102,83 @@ For a full list of renamed classes, check the `.upgrade.yml` definitions in each
The rename won't affect class-based permission codes or database table names.
##### Upgrade references to literal or class table names
In 3.x the class name of any DataObject matched the table name, but in 4.x all classes are namespaced, and it is
necessary to map between table and class for querying the database.
In order to ensure you are using the correct table for any class a new
[DataObjectSchema](api:SilverStripe\ORM\DataObjectSchema) service is available to manage these mappings
(see [versioned documentation](/developer_guides/model/data_model_and_orm)).
For example, the below shows how you would update a query with a hard-coded table name:
```diff
public function countDuplicates($model, $fieldToCheck)
{
$query = new SilverStripe\ORM\Queries\SQLSelect();
+ $table = SilverStripe\ORM\DataObject::getSchema()->tableForField($model, $field);
- $query->setFrom("\"{$model}\"");
+ $query->setFrom("\"{$table}\"");
- $query->setWhere(["\"{$model}\".\"{$field}\"" => $model->$fieldToCheck]);
+ $query->setWhere(["\"{$table}\".\"{$field}\"" => $model->$fieldToCheck]);
return $query->count();
}
```
##### Replacing literal strings with fully qualified class name, or ::class constant
You'll need to update any strings that represent class names and make sure they're fully
qualified. In particular, relationship definitions such as `has_one` and `has_many` will need
to be updated to refer to fully qualified class names.
In configs and with literal PHP strings it is recommended to use the php `::class` constant,
as demonstrated below.
```diff
<?php
+ use SilverStripe\ORM\DataObject;
+ use SilverStripe\Security\Member;
class MyClass extends DataObject
{
private static $has_one = [
- 'Author' => 'Member',
+ 'Author' => Member::class,
];
}
```
In the context of YAML, the magic constant `::class` does not apply. Fully qualified class names must be hard coded.
```diff
-MyObject:
+My\Project\MyObject:
property: value
```
##### Update references to literal classes to use Injector
In many places, classes namespaced in SilverStripe 4.x will still have a non-namespaced service
name that can be accessed via Injector. You should upgrade direct object constructors with
the Injector API.
E.g.
```diff
- $field = new Varchar();
+ $field = Injector::inst()->create('Varchar');
```
#### Migrate your controllers to their own files
The convention for naming controllers is now `[MyPageType]Controller`, where it used to be `[MyPageType]_Controller`. This change was made to be more compatible with the PSR-2 standards.
You can still use, for example, `Page_Controller`, but you will get a deprecation notice. It is
best to change it to `PageController` during your upgrade process. Keep in mind any modules or
other thirdparty code that extend `PageController` are likely to assume that class exists.
By default, a controller for a page type *must* reside in the same namespace as its page. To use different logic, override `SiteTree::getControllerName()`.
#### Upgrade template locations and references
Templates are now much more strict about their locations. You can no longer put a template in an arbitrary
@ -109,6 +196,28 @@ Core template locations have moved - if you're including or overriding these
no longer exists, and instead template locations will be placed in paths that match
the `SilverStripe\Forms` namespace.
#### Upgrade static config settings to `private static`
If you have some class configuration statics defined and they aren't private,
you may find that they don't register anymore. For example, this code, taken from
the `silverstripe/tagfield` module will no longer work in SilverStripe 4.0.
```php
public static $allowed_actions = [
'suggest'
];
```
Changing the visibility to `private` (as per `RequestHandler::$allowed_actions`
visibility) will make it 4.0 compatible.
```diff
-public static $allowed_actions = [
+private static $allowed_actions = [
'suggest'
];
```
#### Upgrade module paths in file references
You should no longer rely on modules being placed in a deterministic folder (e.g. `/framework`),
@ -172,15 +281,89 @@ To ensure consistency, we've also deprecated support for path constants:
The below sections deal with upgrades to specific parts of various API. Projects which rely on certain
API should be upgraded as appropriate using any of the relevant processes documented below.
#### Upgrade references of SS_Log to use PSR-3 logging
One of the great changes that comes with SilverStripe 4 is the introduction of
[PSR-3](http://www.php-fig.org/psr/psr-3/) compatible logger interfaces. This
means we can use thirdparty services like Monolog. `SS_Log` has been replaced
with a logger which can be accessed using the LoggerInterface::class service.
For instance, code which logs errors should be upgraded as below:
```diff
-SS_Log::log('My error message', SS_Log::ERR);
+use Psr\Log\LoggerInterface;
+Injector::inst()->get(LoggerInterface::class)->error('My error message');
```
##### How to customise the default PSR-3 logger
If necessary, you may need to customise either the default logging handler, or
one of the error formatters. For example, if running unit tests you may want to
suppress errors. You can temporarily disable logging by setting a `NullHandler`
```yml
---
Name: custom-dev-logging
After: dev-logging
Only:
environment: dev
---
# Replace default handler with null
SilverStripe\Core\Injector\Injector:
Monolog\Handler\HandlerInterface: Monolog\Handler\NullHandler
```
Alternatively you can customise one or both of the below services:
- `Monolog\Formatter\FormatterInterface.detailed` service, which is used for displaying
detailed error messages useful for developers.
- `Monolog\Formatter\FormatterInterface.friendly` service, which is used to display "error"
page content to visitors of the site who encounter errors.
For example, a custom error page generator could be added as below:
```yml
---
Name: custom-errorpage
After:
- '#loggingformatters'
---
SilverStripe\Core\Injector\Injector:
Monolog\Formatter\FormatterInterface.friendly:
class: WebDesignGroup\ShopSite\Logging\ErrorPageFormatter
```
`WebDesignGroup\ShopSite\Logging\ErrorPageFormatter` should be a class that
implements the `Monolog\Formatter\FormatterInterface` interface.
#### Upgrade `mysite/_config.php`
The globals `$database` and `$databaseConfig` are deprecated. You should upgrade your
site _config.php files to use the `.env` configuration (below).
If you need to configure database details in PHP, use the new `DB::setConfig()` api instead.
`conf/ConfigureFromEnv.php` is also no longer used, and references to this file should be deleted.
You should remove any references to `ConfigureFromEnv.php` in your project, as this file
is no longer necessary.
If you need to configure database details in PHP you should configure these details via `.env` file,
or alternatively (but less recommended) use the new `DB::setConfig()` api.
The global `$project` is deprecated in favour of the configuration setting
`SilverStripe\Core\Manifest\ModuleManifest.project`.
Changes to `mysite/_config.php`:
```diff
<?php
-global $project;
-$project = 'mysite';
-include 'conf/ConfigureFromEnv.php';
```
And also add to `mysite/_config/mysite.yml`:
```yml
SilverStripe\Core\Manifest\ModuleManifest:
project: mysite
```
#### Upgrade of `_ss_environment.php` to `.env` configuration
@ -204,7 +387,7 @@ $_FILE_TO_URL_MAPPING[__DIR__] = 'http://localhost';
// Database
define('SS_DATABASE_CHOOSE_NAME', true);
define('SS_DATABASE_CLASS', 'MySQLDatabase');
define('SS_DATABASE_USERNAME', 'root')
define('SS_DATABASE_USERNAME', 'root');
define('SS_DATABASE_PASSWORD', '');
define('SS_DATABASE_SERVER', '127.0.0.1');
```
@ -548,7 +731,7 @@ In YML format this will be expressed as the below:
`mymodule/lang/en.yml`:
```yaml
```yml
en:
MyObject:
SINGULAR_NAME: 'object'
@ -561,7 +744,7 @@ en:
`extendedmodule/lang/en.yml`:
```yaml
```yml
en:
AnotherSection:
DESCRIPTION: 'This is the description for this section'
@ -612,7 +795,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
```yml
SilverStripe\Filesystem\Flysystem\FlysystemAssetStore:
legacy_paths: true
```
@ -629,7 +812,7 @@ this task is run manually during an explicit migration process, as this process
large amounts of memory and run for an extended time.
```yaml
```yml
File:
migrate_legacy_file: true
```
@ -850,14 +1033,14 @@ use SilverStripe\ORM\FieldType\DBComposite;
class MyAddressField extends DBComposite
{
private static $composite_db = ]
private static $composite_db = [
'Street' => 'Varchar(200)',
'Suburb' => 'Varchar(100)',
'City' => 'Varchar(100)',
'Country' => 'Varchar(100)'
];
public function scaffoldFormField($title = null)
public function scaffoldFormField($title = null, $params = null)
{
new SilverStripe\Forms\TextField($this->getName(), $title);
}
@ -891,39 +1074,6 @@ these references should be replaced with `SQLSelect`. Legacy code which generate
`SQLQuery` can still communicate with new code that expects `SQLSelect` as it is a
subclass of `SQLSelect`, but the inverse is not true.
#### Upgrade code that references table names
A major change in 4.0.0 is that now tables and class names can differ from model to model. In order to
fix a table name, to prevent it being changed (for instance, when applying a namespace to a model)
the `table_name` config can be applied to any DataObject class.
```php
namespace SilverStripe\BannerManager;
use SilverStripe\ORM\DataObject;
class BannerImage extends DataObject
{
private static $table_name = 'BannerImage';
}
```
In order to ensure you are using the correct table for any class a new [DataObjectSchema](api:SilverStripe\ORM\DataObjectSchema) service
is available to manage these mappings (see [versioned documentation](/developer_guides/model/data_model_and_orm)).
```php
public function countDuplicates($model, $fieldToCheck)
{
$table = SilverStripe\ORM\DataObject::getSchema()->tableForField($model, $field);
$query = new SilverStripe\ORM\Queries\SQLSelect();
$query->setFrom("\"{$table}\"");
$query->setWhere(["\"{$table}\".\"{$field}\"" => $model->$fieldToCheck]);
return $query->count();
}
```
#### Upgrade BuildTask classes
Similarly to the `$table_name` configuration property for DataObjects, you should define a `private static $segment` for `BuildTask`
@ -1105,10 +1255,43 @@ These methods are deprecated:
#### Implementation of ownership API
In order to support the recursive publishing of dataobjects, a new API has been developed to allow
developers to declare dependencies between objects. See the
[versioned documentation](/developer_guides/model/versioning) for more information.
developers to declare dependencies between objects. This is done to ensure that the published state
of linked components are consistent with their "owner." Without the concept of ownership, these linked
components could be implicitly exposed on the frontend, which may not align with the intent of the
content author.
By default all versioned dataobjects will automatically publish objects that they own.
For instance, on a products page which has a list of products, the products should not be published unless the products page is, too. The ownership API solves this by allowing you to declare
a two-way relationship between objects, typically, but not necessarily, linked by a database relationship
(`has_many`, `many_many`, etc.).
```php
class ProductPage extends Page
{
private static $has_many = [
'Products' => Product::class
];
private static $owns = [
'Products'
];
}
class Product extends DataObject
{
private static $extensions = [
Versioned::class
];
private static $has_one = [
'Parent' => ProductPage::class
];
}
```
If your objects are linked by something other than a database relationship, for instance, a custom
getter that is computed at runtime, the same rules can be applied, as long as you provide an `$owned_by`
setting on the child object.
For more information, see the [DataObject ownership](https://docs.silverstripe.org/en/4/developer_guides/model/versioning/#dataobject-ownership) documentation and the [versioning](/developer_guides/model/versioning) documentation
#### ChangeSet batch publishing
@ -1210,7 +1393,7 @@ specific functions.
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
```yml
SilverStripe\Security\Authenticator;
authenticators:
- MyVendor\MyModule\MyAuthenticator
@ -1218,7 +1401,7 @@ SilverStripe\Security\Authenticator;
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
```yml
SilverStripe\Security\Authenticator:
default_authenticator: SilverStripe\Security\MemberAuthenticator
```
@ -1254,7 +1437,6 @@ 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()`).
```diff
-$cache = Cache::factory('myCache');
+use Psr\SimpleCache\CacheInterface;
@ -1281,10 +1463,9 @@ and have a slightly different API (e.g. `set()` instead of `save()`).
+$cache->delete('myCacheKey');
```
With the necessary minimal config in `_config/mycache.yml`
With the necessary minimal config in _config/mycache.yml
```yaml
```yml
---
Name: mycache
---
@ -1295,13 +1476,12 @@ SilverStripe\Core\Injector\Injector:
namespace: 'mycache'
```
##### Configuration Changes
Caches are now configured through dependency injection services instead of PHP.
See our ["Caching" docs](/developer-guides/performance/caching) for more details.
Before (`mysite/_config.php`):
Before (mysite/_config.php):
```php
Cache::add_backend(
@ -1317,9 +1497,9 @@ Cache::add_backend(
Cache::pick_backend('primary_memcached', 'any', 10);
```
After (`mysite/_config/config.yml`):
After (mysite/_config/config.yml):
```yaml
```yml
---
After:
- '#corecache'
@ -1335,6 +1515,158 @@ SilverStripe\Core\Injector\Injector:
client: '%$MemcachedClient
```
### <a name="usercode-style-upgrades"></a>User-code style upgrades
Although it is not mandatory to upgrade project code to follow SilverStripe and
PSR-2 standard it is highly recommended to ensure that code is consistent. The below sections
will assist you with bringing yourself up to speed.
Please note that before upgrading user code style it is necessary to run the standard upgrade path
to fix references and usages of framework API.
#### Upgrading user-code to use namespaces
Upgrading code to use namespaces is quite a complex process, and as such we have provided
several development tools and processes to help make the upgrade user friendly and as
automated as possible.
##### Using the upgrader tool to automatically apply namespaces
The [upgrader tool](https://github.com/silverstripe/silverstripe-upgrader/) provides a feature
to not only automatically namespace code, but will provide automatic upgrade of other code
references to those classes.
Use the below to setup upgrader, and apply a namespace to a given code folder.
```
composer global require silverstripe/upgrader
cd ~/Project/Root
~/.composer/vendor/bin/upgrade-code add-namespace "WebDesignGroup\ShopSite" ./mysite/code --recursive --write
```
If you want to do a dry-run, omit the `--write` option to see a preview of a diff of
all changed project files.
This task will do the following:
- Add the given namespace to all files in the code class, and subdirectories.
- Sub-namespaces will be applied based on directory structure
- All references to classes in any namespaced files will be safely retained with additional `use` directives
added as necessary.
- Register all namespaced classese in a mysite/.upgrade.yml file for migration of other code
This task will not do the following, and must be done manually:
- Adding `table_name` to any namespaced classes
- Upgrade other references to namespaced classes outside of this folder
- Migrate any database table records
Please see the following steps for more information.
##### Using the upgrader tool to update references to namespaced user classes
Once a project has been namespaced all newly renamed classes will have a mapping included in the `mysite/.upgrade.yml`
file. If you have any user-code that references these, you may need to run the upgrader again (as you did did
to upgrade your project to namespaced framework classe).
```
composer global require silverstripe/upgrader
cd ~/Project/Root
~/.composer/vendor/bin/upgrade-code upgrade ./othercode --write
```
##### Updating custom dataobjects to use existing table names
Once you have namespaced your user code it will be necessary to customise the `table_name` config
for your dataobjects, in order to ensure the correct table is used after upgrade. It is recommended
to point this to the base name of the class, excluding namespace, as in 3.x.
```diff
namespace WebDesignGroup\ShopSite;
use SilverStripe\ORM\DataObject;
use Page;
class GalleryPage extends Page
{
+ private static $table_name = 'GalleryPage';
}
```
##### Class name remapping
If you've namespaced one of your custom page types, you may notice a message in the CMS
telling you it's obsolete. This is likely because the `ClassName`
field in the `SiteTree` table still contains the singular model name, e.g. `GalleryPage`
and that when you change it to `WebDesignGroup\ShopSite\GalleryPage` then everything
works again.
The `dev/build` task is configured to look for a legacy class name mapping
configuration setting and will update this for you automatically. You can use
this to add DB upgrading rules for your own classes.
For example, you could upgrade references to the newly namespaced Banner class by adding
this to your `mysite/_config/upgrade.yml` file:
```yml
SilverStripe\ORM\DatabaseAdmin:
classname_value_remapping:
GalleryPage: WebDesignGroup\ShopSite\GalleryPage
```
The next time you run a dev/build the class name for all `GalleryPage` pages will
be automatically updated to the new `WebDesignGroup\ShopSite\GalleryPage`
#### Using php code checker to automatically update to PSR-2
You can use the [php codesniffer](https://github.com/squizlabs/PHP_CodeSniffer) tool
to not only detect and lint PSR-2 coding errors, but also do some minimal automatic
code style migration.
- Install the necessary library:
`composer require squizlabs/php_codesniffer`
- Copy silverstripe standards config file from framework/phpcs.xml to your project root:
`cp ./framework/phpcs.xml ./phpcs.xml`
- Run the automatic upgrade tool on your code folder
`vendor/bin/phpcbf ./mysite/code`
- Run the automatic linting tool to detect and manually fix other errors:
`vendor/bin/phpcs ./mysite/code`
Repeat the final step and manually repair suggested changes, as necessary,
until you no longer have any linting issues.
#### Upgrade user-code to use PSR-4 autoloading
While not critical to an upgrade, SilverStripe 4.0 has adopted the [PS-4 autoloading](http://www.php-fig.org/psr/psr-4/)
standard for the core modules, so it's probably a good idea to be consistent.
You can implement this in your composer configuration like so:
```json
{
"autoload": {
"psr-4": {
"WebDesignGroup\\ShopSite\\": "mysite/src/"
}
}
}
```
Now you just need to ensure that each class site in the correct folder location
(including case sensitivity) to match its namespace. For example,
`WebDesignGroup\ShopSite\Model\GalleryItem.php` should live at `mysite/src/Model/GalleryItem.php`.
Note that you dont have to use `src/` as the root folder name. You can continue
to use `code/` if you want to. SilverStripe has adopted the PSR-4 approach and
have also started to use `src/` as a default folder location instead of
`code/`. If youre going to change, it's probably a good time to do it while you're
upgrading.
For examples, take a look at the file/folder structure and to the
`composer.json` configuration in either the `framework` or `cms` modules.
Please note that there are changes to template structure which in some cases
require templates to be in a folder location that matches the namespace of the class
that it belongs to, e.g. `themes/mytheme/templates/MyVendor/Foobar/Model/MyModel.ss`.
## <a name="api-changes"></a>API Changes
### <a name="overview-general"></a>General and Core API
@ -2056,10 +2388,8 @@ New `TimeField` methods replace `getConfig()` / `setConfig()`
* `i18n::get_language_code()` removed.
* `i18n::get_common_locales()` removed.
* `i18n.common_locales` config removed
### <a name="overview-mailer"></a>Email and Mailer
#### <a name="overview-mailer-api"></a>Email Additions / Changes
#### <a name="overview-mailer"></a>Email Additions / Changes
* `Mailer` converted to an interface
* `SwfitMailer` added as new default mailer
@ -2080,7 +2410,7 @@ New `TimeField` methods replace `getConfig()` / `setConfig()`
* `SapphireTest::$extraDataObjects` renamed to `SapphireTest::$extra_dataobjects` and made static
* `SapphireTest::$extraControllers` renamed to `SapphireTest::$extra_controllers` and made static
### <a name="overview-testing"></a>Security
### <a name="overview-security"></a>Security
* `LoginForm` now has an abstract method `getAuthenticatorName()`. If you have made subclasses of this,
you will need to define this method and return a short name describing the login method.