mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
First pass at upgrade docs
This commit is contained in:
parent
d7095c2213
commit
2b9369895d
@ -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.
|
[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
|
* 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.
|
[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
|
||||||
|
|
||||||
## <a name="upgrading"></a>Upgrading
|
## <a name="upgrading"></a>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.
|
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
|
||||||
|
<!-- before -->
|
||||||
|
private static $has_one = [
|
||||||
|
'MyRelation' => 'MyObject',
|
||||||
|
];
|
||||||
|
<!-- after -->
|
||||||
|
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
|
||||||
|
<!-- before -->
|
||||||
|
MyObject:
|
||||||
|
property: value
|
||||||
|
<!-- after -->
|
||||||
|
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
|
#### Upgrade template locations and references
|
||||||
|
|
||||||
Templates are now much more strict about their locations. You can no longer put a template in an arbitrary
|
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
|
no longer exists, and instead template locations will be placed in paths that match
|
||||||
the `SilverStripe\Forms` namespace.
|
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
|
||||||
|
<!-- before -->
|
||||||
|
public static $allowed_actions = [
|
||||||
|
'suggest'
|
||||||
|
];
|
||||||
|
<!-- after -->
|
||||||
|
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
|
||||||
|
<!-- before -->
|
||||||
|
SS_Log::log('My error message', SS_Log::ERR);
|
||||||
|
<!-- after -->
|
||||||
|
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
|
#### Upgrade module paths in file references
|
||||||
|
|
||||||
You should no longer rely on modules being placed in a deterministic folder (e.g. `/framework`),
|
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.
|
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
|
||||||
|
<!-- before -->
|
||||||
|
global $project;
|
||||||
|
$project = 'mysite';
|
||||||
|
<!-- after -->
|
||||||
|
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.
|
is no longer necessary.
|
||||||
|
|
||||||
#### Upgrade of `_ss_environment.php` to `.env` configuration
|
#### 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
|
`SQLQuery` can still communicate with new code that expects `SQLSelect` as it is a
|
||||||
subclass of `SQLSelect`, but the inverse is not true.
|
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
|
#### Upgrade BuildTask classes
|
||||||
|
|
||||||
Similarly to the `$table_name` configuration property for DataObjects, you should define a `private static $segment` for `BuildTask`
|
Similarly to the `$table_name` configuration property for DataObjects, you should define a `private static $segment` for `BuildTask`
|
||||||
|
Loading…
Reference in New Issue
Block a user