From 2b9369895d6f4cc0e513a384eb4be8d811d9d712 Mon Sep 17 00:00:00 2001 From: Aaron Carlino Date: Thu, 13 Jul 2017 14:57:28 +1200 Subject: [PATCH] First pass at upgrade docs --- docs/en/04_Changelogs/4.0.0.md | 280 +++++++++++++++++++++++++++++---- 1 file changed, 246 insertions(+), 34 deletions(-) diff --git a/docs/en/04_Changelogs/4.0.0.md b/docs/en/04_Changelogs/4.0.0.md index c280b6320..e0de98e65 100644 --- a/docs/en/04_Changelogs/4.0.0.md +++ b/docs/en/04_Changelogs/4.0.0.md @@ -59,6 +59,7 @@ guide developers in preparing existing 3.x code for compatibility with 4.0 [behat extension](https://github.com/silverstripe/silverstripe-behat-extension) for more information. * The `GDBackend` and `ImagickBackend` classes have been replaced by a unified `InterventionBackend` which uses the [intervention/image](https://github.com/intervention/image) library to power manipualations. +* Page types and their controllers can no longer be collated in a single file ## Upgrading @@ -92,6 +93,138 @@ 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. +##### Use ::class whenever possible + +Prefer: + ```php + private static $has_one = [ + 'MyObject' => MyObject::class + ]; + ``` + to + + ```php +private static $has_one = [ + 'MyObject' => 'My\Project\MyObject' +]; +``` + +##### Use Injector wherever you can + +This can help with reducing changes from 3.x to 4.x by allowing your code to +continue to use `MyClassName`, while you tell the injector what the FQ class name is: + +```yaml +SilverStripe\Core\Injector\Injector: + MyClassName: + class: Me\MyModule\MyClassName # fully qualified! +``` + +##### Namespaced table names + +Keep in mind that table names are namespaced too. To ease data migrations, use the `$table_name` config property of your `DataObject` subclasses. +This allows you to customise the table name to something simpler, or perhaps identical to its 3.x name. +```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(); +} +``` + +##### Class name remapping + +If you've namespaced one of your custom page types then loaded up the CMS and see +a message telling you it's obsolete. This is likely because the `ClassName` +field in the `SiteTree` table still contains the singular model name, e.g. `BlogPost` +and that when you change it to `SilverStripe\Blog\Model\BlogPost` then everything +works again. + +Luckily the `dev/build` task is configured to look for a legacy class name mapping +configuration setting and will update this for you automatically. For an example +take a look at `_config/legacy.yml` in the CMS module. For the Blog module we used +this: + +```yaml +SilverStripe\ORM\DatabaseAdmin: + classname_value_remapping: + Blog: SilverStripe\Blog\Model\Blog + BlogCategory: SilverStripe\Blog\Model\BlogCategory + BlogPost: SilverStripe\Blog\Model\BlogPost + BlogTag: SilverStripe\Blog\Model\BlogTag +``` + +##### Other namespacing gotchas + +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. + +```ss + +private static $has_one = [ + 'MyRelation' => 'MyObject', +]; + +private static $has_one = [ + 'MyRelation' => MyObject::class, +]; +``` + +In the context of YAML, the magic constant `::class` does not apply. Class names must be hard coded. + +```ss + +MyObject: + property: value + +My\Project\MyObject: + property: value +``` + +When dealing with custom database queries, you need to be judicious about replacing table +names expressed literally. The upgrader will automatically replace anything that looks +like an unqualified class name with a fully qualified one, but a fully qualified +class name may not be the same as the table name, as explained above, in the case of +`$table_name`. + +For example: +```php +->innerJoin('BlogPost', sprintf('"BlogPost%s"."ID" = "SiteTree%s"."ID"', $stage, $stage)) +``` +The upgrader will replace `BlogPost` with `SilverStripe\\Blog\\Model\BlogPost` as the fully qualified class name. In the case of innerJoin(), it asks for a table name which have remained the same as SilverStripe 3.x, so this should not be namespaced. This of course depends on what you’ve configured your table name to be. + +A better option is to replace it with `DataObject::getSchema()->tableName(BlogPost::class)` as long as the class has already been imported into your current namespace. + + + + +#### 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. Best to change it to `PageController` during your upgrade process. + +By default, a controller for a page type *must* reside in the same namespace as its page. To break this convention, 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 +242,106 @@ 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 your member statics + +If you've got some class configuration statics defined and they aren't private, +you may find that they don't register any more. 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. + +```ss + +public static $allowed_actions = [ + 'suggest' +]; + +private static $allowed_actions = [ + 'suggest' +]; +``` + +#### Get on board with 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 into your composer configuration like so: + +```js + ... + "autoload": { + "psr-4": { + "SilverStripe\\Blog\\": "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, + `SilverStripe\Blog\Model\BlogPost` should live at `src/Model/BlogPost.php`. + +Note that you don’t 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 you’re 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 compared 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 class's namespace +that it belongs to, e.g. `themes/mytheme/templates/MyVendor/Foobar/Model/MyModel.ss`. + +#### Get on board with PSR-# logging, too +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. + +Note that the old `SS_Log` class has been renamed to `SilverStripe\Logging\Log`, +but has also had most of its logic stripped out and the remainder is marked as +deprecated. + +```ss + +SS_Log::log('My error message', SS_Log::ERR); + +Injector::inst()->get('Logger')->error('My error message'); +``` + +If you call that multiple times in one class, you might want to add a getter +method to centralize it a little - but you probably should've had that already +in this case! + +##### Unit tests and PSR-3 loggers + +If you've got some code that you've tested originally using `SS_Log`, and now moved +to using a Monolog Logger then you may notice occasions where your unit tests pass +but output a bunch of warnings, debug information, etc. to `stderr`. This is because + Monolog will use `stderr` as the default Handler unless you specify one. + +This isn't so much a regression as simply that you're now seeing things happening +in your code that didn't used to happen. You have a couple of options here. You can either: + +* Address that there's possibly a logic problem in your unit tests +* Push a conveniently named `NullHandler` to monolog to quiet it. + +```php +$logger = Injector::inst()->get('Logger'); +$logger->pushHandler(new \Monolog\Handler\NullHandler); +``` + +It is not recommended to do this in your actual class. It's better to make use of +a convenient public getter method and configure this from the `setUp()` method of +your unit test class. + #### Upgrade module paths in file references You should no longer rely on modules being placed in a deterministic folder (e.g. `/framework`), @@ -179,7 +412,19 @@ 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. -You should remove any references to `ConfigureFromEnv.php` in your project, as this file +The global `$project` is deprecated in favour of the configuration setting +`SilverStripe\Core\Manifest\ModuleManifest.project`. + +```ss + +global $project; +$project = 'mysite'; + +SilverStripe\Core\Manifest\ModuleManifest: + project: mysite +``` + +Lastly, you should remove any references to `ConfigureFromEnv.php` in your project, as this file is no longer necessary. #### Upgrade of `_ss_environment.php` to `.env` configuration @@ -891,39 +1136,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`