mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
MINOR Upgrading notes
This commit is contained in:
parent
d88a68016f
commit
f8d38a332c
@ -13,6 +13,106 @@
|
|||||||
|
|
||||||
## Upgrading ##
|
## Upgrading ##
|
||||||
|
|
||||||
|
### New ORM: More flexible and expressive querying via `DataList` ###
|
||||||
|
|
||||||
|
The new "fluent" syntax to retrieve ORM records allows for a more
|
||||||
|
expressive notation (instead of unnamed arguments).
|
||||||
|
|
||||||
|
:::php
|
||||||
|
// before
|
||||||
|
DataObject::get('Member', '"FirstName" = \'Sam'\', '"Surname" ASC");
|
||||||
|
// after
|
||||||
|
DataList::create('Member')->filter(array('FirstName' => 'Sam'))->sort('Surname');
|
||||||
|
|
||||||
|
The underlying record retrieval and management is rewritten from scratch, and features
|
||||||
|
lazy loading which fetches only the records it needs, as late as possible.
|
||||||
|
In order to retrieve all ORM records manually (as the previous ORM would've done),
|
||||||
|
please use `DataList->toArray()`.
|
||||||
|
|
||||||
|
The old getters (`DataObject::get()`, `DataObject:;get_one()`, `DataObject::get_by_id()`)
|
||||||
|
are now deprecated, but continue to operate. Instead of a `DataObjectSet`, they'll
|
||||||
|
now return a `DataList`.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
// before
|
||||||
|
DataObject::get_one('Member', '"Email" = \'someone@example.com\'');
|
||||||
|
// after
|
||||||
|
DataList::create('Member')->filter('Email', 'someone@example.com')->First();
|
||||||
|
|
||||||
|
:::php
|
||||||
|
// before
|
||||||
|
DataObject::get_by_id('Member', 5);
|
||||||
|
// after
|
||||||
|
DataList::create('Member')->byID(5);
|
||||||
|
|
||||||
|
Note that they will return a `DataList` even if they're empty, so if you want to check
|
||||||
|
for the presence of records, please call the count() method on the `DataList`:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
// before
|
||||||
|
if(!DataObject::get('SiteTree', '"ParentID" = 5')) echo "Page 5 has no children";
|
||||||
|
// after
|
||||||
|
if(!DataObject::get('SiteTree', '"ParentID" = 5')->count()) echo "Page 5 has no children";
|
||||||
|
|
||||||
|
See the ["datamodel" documentation](../../topics/datamodel) for more details.
|
||||||
|
|
||||||
|
### New ORM: Changes to manipulation of SQL queries ###
|
||||||
|
|
||||||
|
In the 2.4 ORM it was sometimes necessary to bypass the ORM for performance reasons. For example,
|
||||||
|
this command would have been intolerably slow:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
DataList::create('SiteTree')->count();
|
||||||
|
|
||||||
|
The 3.0 ORM is more intelligent gives you tools you need to create high-performance code without
|
||||||
|
bypassing the ORM:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
// before
|
||||||
|
echo DB::query("SELECT COUNT(*) FROM \"SiteTree\"")->value();
|
||||||
|
// after
|
||||||
|
echo DataList::create('SiteTree')->count()
|
||||||
|
|
||||||
|
Both `extendedSQL()` and `buildSQL()` have been deprecated. There is not currently any way of
|
||||||
|
overriding the query generation code equivalent to overriding `buildSQL()` in 2.4, but this facility
|
||||||
|
was error prone. If you need to access the `SQLQuery` object, you can call `->dataQuery()->query()`
|
||||||
|
on any DataList. Note that modifications to this query will **not** be passed back into the DataList.
|
||||||
|
|
||||||
|
:::php
|
||||||
|
// before
|
||||||
|
$query = singleton('SiteTree')->extendedSQL('ParentID = 5');
|
||||||
|
// after
|
||||||
|
$query = DataList::create('SiteTree')->filter('ParentID', 5)->dataQuery()->query();
|
||||||
|
|
||||||
|
We advise that you keep this kind of code to a minimum and that you use the DataList wherever possible.
|
||||||
|
If you find yourself needing to bypass the ORM in SilverStripe 3, we suggest you raise this
|
||||||
|
as a discussion topic on silverstripe-dev@groups.google.com, as we may want to add more tools to
|
||||||
|
the ORM to help you.
|
||||||
|
|
||||||
|
### New ORM: Better encapsulation of relationship queries with `RelationList` ###
|
||||||
|
|
||||||
|
The abstract `RelationList` class and its implementations `ManyManyList` and `HasManyList`
|
||||||
|
are replacing the `ComponentSet` API, which is only relevant if you have instanciated these manually.
|
||||||
|
Relations are retrieved through the same way (e.g. `$myMember->Groups()`).
|
||||||
|
|
||||||
|
### InnoDB driver for existing and new tables on MySQL (instead of MyISAM) ###
|
||||||
|
|
||||||
|
SilverStripe has traditionally created all MySQL tables with the MyISAM storage driver,
|
||||||
|
mainly to ensure a fulltext search based on MySQL works out of the box.
|
||||||
|
Since then, the framework has gained many alternatives for fulltext search
|
||||||
|
([sphinx](https://github.com/silverstripe/silverstripe-sphinx), [solr](https://github.com/nyeholt/silverstripe-solr), etc.), and relies more on database transactions and other features not available in MyISAM.
|
||||||
|
|
||||||
|
This change convert tables on existing databases when `dev/build` is called,
|
||||||
|
unless the `FullTextSearch` feature is enabled. In order to disable this behaviour,
|
||||||
|
you have to add the following code to your `_config.php` BEFORE running a `dev/build`:
|
||||||
|
|
||||||
|
:::php
|
||||||
|
DataObject::$create_table_options['MySQLDatabase] = 'ENGINE=MyISAM';
|
||||||
|
|
||||||
|
As with any SilverStripe upgrade, we recommend database backups before calling `dev/build`.
|
||||||
|
See [mysql.com](http://dev.mysql.com/doc/refman/5.5/en/converting-tables-to-innodb.html) for details on the conversion.
|
||||||
|
Note: MySQL has made InnoDB the default engine in its [5.5 release](http://dev.mysql.com/doc/refman/5.5/en/innodb-storage-engine.html).
|
||||||
|
|
||||||
### GridField: Replacement for TableListField and ComplexTableField ###
|
### GridField: Replacement for TableListField and ComplexTableField ###
|
||||||
|
|
||||||
We have a new component for managing lists of objects: The `[GridField](/topics/grid-field)`.
|
We have a new component for managing lists of objects: The `[GridField](/topics/grid-field)`.
|
||||||
@ -58,7 +158,7 @@ The template engine has been completely rewritten, and although it is generally
|
|||||||
and some features have been deprecated. See the [template upgrading guide](/reference/templates-upgrading-guide) and the
|
and some features have been deprecated. See the [template upgrading guide](/reference/templates-upgrading-guide) and the
|
||||||
[template reference](/reference/templates) for more information.
|
[template reference](/reference/templates) for more information.
|
||||||
|
|
||||||
#### Removed view-specific accessors from ViewableData ####
|
### Removed view-specific accessors from ViewableData ####
|
||||||
|
|
||||||
Several methods in ViewableData that were originally added to expose values to the template language were moved,
|
Several methods in ViewableData that were originally added to expose values to the template language were moved,
|
||||||
in order to stop polluting the namespace. These were sometimes called by project-specific PHP code too, and that code
|
in order to stop polluting the namespace. These were sometimes called by project-specific PHP code too, and that code
|
||||||
@ -126,42 +226,11 @@ please create a new CSS file and link it into the CMS via `[api:LeftAndMain::req
|
|||||||
|
|
||||||
### Built-in Javascript validation removed ###
|
### Built-in Javascript validation removed ###
|
||||||
|
|
||||||
Built-in client-side form validation using behaviour.js has been removed, and is no longer supported.
|
Built-in client-side form validation using `Validator.js` and `behaviour.js` has been removed, and is no longer supported.
|
||||||
|
|
||||||
Server-side validation remains. Developers are encouraged to use custom Javascript validation on their
|
Server-side validation remains. Developers are encouraged to use custom Javascript validation on their
|
||||||
forms if requiring client-side validation.
|
forms if requiring client-side validation.
|
||||||
|
You don't need to explicitly disable JS validation through `Validator::set_javascript_validation_handler()`
|
||||||
These classes/files have been removed:
|
any longer (the method is deprecated).
|
||||||
|
|
||||||
Validator.js
|
|
||||||
CustomRequiredFields.php
|
|
||||||
|
|
||||||
These functions are now deprecated and will throw a notice if used:
|
|
||||||
|
|
||||||
Validator::set_javascript_validation_handler()
|
|
||||||
Validator::get_javascript_validator_handler()
|
|
||||||
Validator::setJavascriptValidationHandler()
|
|
||||||
|
|
||||||
These functions have been removed:
|
|
||||||
|
|
||||||
Validator::javascript() (abstract function)
|
|
||||||
Validator::includeJavascriptValidation()
|
|
||||||
FormField::jsValidation()
|
|
||||||
AjaxUniqueTextField::jsValidation()
|
|
||||||
ConfirmedPasswordField::jsValidation()
|
|
||||||
CreditCardField::jsValidation()
|
|
||||||
CurrencyField::jsValidation()
|
|
||||||
CustomRequiredFields::javascript()
|
|
||||||
DateField::jsValidation()
|
|
||||||
DatetimeField::jsValidation()
|
|
||||||
EmailField::jsValidation()
|
|
||||||
FieldGroup::jsValidation()
|
|
||||||
FormField::jsValidation()
|
|
||||||
NumericField::jsValidation()
|
|
||||||
PhoneNumberField::jsValidation()
|
|
||||||
RequiredFields::javascript()
|
|
||||||
TableField::jsValidation()
|
|
||||||
TimeField::jsValidation()
|
|
||||||
|
|
||||||
### FormField consistently adds classes to HTML elements ###
|
### FormField consistently adds classes to HTML elements ###
|
||||||
|
|
||||||
@ -235,6 +304,11 @@ IP restrictions for group memberships in the "Security" section were a rarely us
|
|||||||
and cluttered up the interface. We've decided to move it to a separate module
|
and cluttered up the interface. We've decided to move it to a separate module
|
||||||
called [securityextras](https://github.com/silverstripe-labs/silverstripe-securityextras).
|
called [securityextras](https://github.com/silverstripe-labs/silverstripe-securityextras).
|
||||||
To continue using these restrictions, just install the module - no data migration required.
|
To continue using these restrictions, just install the module - no data migration required.
|
||||||
|
|
||||||
|
### Moved comments system into new 'comments' module ###
|
||||||
|
|
||||||
|
This affects websites which have comments enabled, through the `$Comments`
|
||||||
|
placeholder and the `PageComment` class. See the ['comments' module](https://github.com/silverstripe/silverstripe-comments). To continue using comments, simply install the module - no data migration required.
|
||||||
|
|
||||||
### Removed "auto-merging" of member records from `Member->onBeforeWrite()`
|
### Removed "auto-merging" of member records from `Member->onBeforeWrite()`
|
||||||
|
|
||||||
@ -260,26 +334,56 @@ Breadcrumbs have been altered to be their own template. In the process of this,
|
|||||||
SiteTree::$breadcrumbs_delimiter has been removed. To customise breadcrumbs now, create a template
|
SiteTree::$breadcrumbs_delimiter has been removed. To customise breadcrumbs now, create a template
|
||||||
BreadcrumbsTemplate.ss from cms/template to your theme or application.
|
BreadcrumbsTemplate.ss from cms/template to your theme or application.
|
||||||
|
|
||||||
|
### Deprecation API ###
|
||||||
|
|
||||||
|
There is a new deprecation API that generates deprecation notices. Calls to Deprecated methods
|
||||||
|
will only produce errors if the API was deprecated in the release equal to or earlier than the
|
||||||
|
"notification version".
|
||||||
|
|
||||||
|
`sapphire/_config.php` currently contains a call to throw notices calls to all methods deprecated
|
||||||
|
in 3.0.
|
||||||
|
|
||||||
|
Deprecation::notification_version('3.0.0');
|
||||||
|
|
||||||
|
If you change the notification version to 3.0.0-dev, then only methods deprecated in older versions
|
||||||
|
(e.g. 2.4) will trigger notices, and the other methods will silently pass. This can be useful if
|
||||||
|
you don't yet have time to remove all calls to deprecated methods.
|
||||||
|
|
||||||
|
Deprecation::notification_version('3.0.0-dev');
|
||||||
|
|
||||||
### Deprecated Classes ###
|
### Deprecated Classes ###
|
||||||
|
|
||||||
|
* `ComplexTableField`: Use `GridField` instead
|
||||||
|
* `DataObjectDecorator`: Use `DataExtension` instead
|
||||||
|
* `DataObjectSet`: Use `DataList` instead
|
||||||
* `FileIframeField`: Use `UploadField`
|
* `FileIframeField`: Use `UploadField`
|
||||||
|
* `HasManyComplexTableField`: Use `GridField` instead
|
||||||
* `ImageField`: Use `UploadField` with `$myField->allowedExtensions = array('jpg', 'gif', 'png')`
|
* `ImageField`: Use `UploadField` with `$myField->allowedExtensions = array('jpg', 'gif', 'png')`
|
||||||
|
* `ManyManyComplexTableField`: Use `GridField` instead
|
||||||
|
* `SimpleImageField`: Use `FileField` or `UploadField` with `setAllowedExtensions()`
|
||||||
|
* `TableListField`: Use `GridField` instead
|
||||||
|
|
||||||
### Renamed Classes ###
|
### Renamed Classes ###
|
||||||
|
|
||||||
|
* `CMSMainMarkingFilter`: Use `CMSSiteTreeFilter_Search` instead *
|
||||||
* `DataObjectDecorator`: Use `DataExtension` instead (the class doesn't implement the [GOF "Decorator" pattern](http://en.wikipedia.org/wiki/Decorator_pattern))
|
* `DataObjectDecorator`: Use `DataExtension` instead (the class doesn't implement the [GOF "Decorator" pattern](http://en.wikipedia.org/wiki/Decorator_pattern))
|
||||||
* `MySQLFulltextSearchable`: Use `FulltextSearchable` instead
|
* `MySQLFulltextSearchable`: Use `FulltextSearchable` instead
|
||||||
* `CMSMainMarkingFilter`: Use `CMSSiteTreeFilter_Search` instead *
|
|
||||||
|
|
||||||
### Removed Classes ###
|
### Removed Classes ###
|
||||||
|
|
||||||
|
* `Archive`, `TarballArchive`: If you make use of these, copy the classes from 2.4 into your project.
|
||||||
|
* `AssetTableField`: Use `GridField` with `GridFieldConfig_RelationEditor` instead
|
||||||
|
* `ComponentSet`: Use `ManyManyList` or `HasManyList`
|
||||||
|
* `CustomRequiredFields`: Use `RequiredFields`
|
||||||
|
* `DataObjectLog`: There is no replacement for this.
|
||||||
|
* `DataObjectSet`: Use `ArrayList` or `DataList` instead
|
||||||
|
* `FieldSet`: Use `FieldList` instead
|
||||||
|
* `GeoIP`: Moved to separate ["geoip" module](https://github.com/silverstripe-labs/silverstripe-geoip)
|
||||||
|
* `MemberTableField`: Use `GridField` with `GridFieldConfig_RelationEditor` instead
|
||||||
|
* `Notifications`: If you make use of this, copy the classes from 2.4 into your project.
|
||||||
|
* `NZGovtPasswordValidator`: Moved to ["securityextras" module](https://github.com/silverstripe-labs/silverstripe-securityextras)
|
||||||
* `QueuedEmail`, `QueuedEmailDispatchTask`: If you make use of these, copy the classes from 2.4 into your project.
|
* `QueuedEmail`, `QueuedEmailDispatchTask`: If you make use of these, copy the classes from 2.4 into your project.
|
||||||
* `RestrictedTextField`, `UniqueTextField`, `UniqueRestrictedTextField`, `AutocompleteTextField`, `ConfirmedFormAction`: Use custom fields instead
|
* `RestrictedTextField`, `UniqueTextField`, `UniqueRestrictedTextField`, `AutocompleteTextField`, `ConfirmedFormAction`: Use custom fields instead
|
||||||
|
* `SQLMap`: Use `SS_Map` instead
|
||||||
* `TreeSelectorField`: Use `TreeDropdownField` instead.
|
* `TreeSelectorField`: Use `TreeDropdownField` instead.
|
||||||
* `Notifications`: If you make use of this, copy the classes from 2.4 into your project.
|
|
||||||
* `Archive`, `TarballArchive`: If you make use of these, copy the classes from 2.4 into your project.
|
|
||||||
* `XML`: Use PHP's built-in SimpleXML instead
|
* `XML`: Use PHP's built-in SimpleXML instead
|
||||||
* `DataObjectLog`: There is no replacement for this.
|
|
||||||
* `GeoIP`: Moved to separate ["geoip" module](https://github.com/silverstripe-labs/silverstripe-geoip)
|
|
||||||
* `NZGovtPasswordValidator`: Moved to ["securityextras" module](https://github.com/silverstripe-labs/silverstripe-securityextras)
|
|
||||||
* `MemberTableField`: Use GridField with GridFieldConfig_RelationEditor instead
|
|
||||||
|
@ -18,122 +18,6 @@ See [3.0.0 upgrading guide](../3.0.0) for more details.
|
|||||||
|
|
||||||
## Upgrading ##
|
## Upgrading ##
|
||||||
|
|
||||||
### New ORM: More flexible and expressive querying via `DataList` ###
|
|
||||||
|
|
||||||
The new "fluent" syntax to retrieve ORM records allows for a more
|
|
||||||
expressive notation (instead of unnamed arguments).
|
|
||||||
|
|
||||||
// before
|
|
||||||
DataObject::get('Member', '"FirstName" = \'Sam'\', '"Surname" ASC");
|
|
||||||
// after
|
|
||||||
DataList::create('Member')->filter(array('FirstName' => 'Sam'))->sort('Surname');
|
|
||||||
|
|
||||||
The underlying record retrieval and management is rewritten from scratch, and features
|
|
||||||
lazy loading which fetches only the records it needs, as late as possible.
|
|
||||||
In order to retrieve all ORM records manually (as the previous ORM would've done),
|
|
||||||
please use `DataList->toArray()`.
|
|
||||||
|
|
||||||
The old getters (`DataObject::get()`, `DataObject:;get_one()`, `DataObject::get_by_id()`)
|
|
||||||
are now deprecated, but continue to operate. Instead of a `DataObjectSet`, they'll
|
|
||||||
now return a `DataList`.
|
|
||||||
|
|
||||||
// before
|
|
||||||
DataObject::get_one('Member', '"Email" = \'someone@example.com\'');
|
|
||||||
// after
|
|
||||||
DataList::create('Member')->filter('Email', 'someone@example.com')->First();
|
|
||||||
|
|
||||||
// before
|
|
||||||
DataObject::get_by_id('Member', 5);
|
|
||||||
// after
|
|
||||||
DataList::create('Member')->byID(5);
|
|
||||||
|
|
||||||
Note that they will return a `DataList` even if they're empty, so if you want to check
|
|
||||||
for the presence of records, please call the count() method on the `DataList`:
|
|
||||||
|
|
||||||
// before
|
|
||||||
if(!DataObject::get('SiteTree', '"ParentID" = 5')) echo "Page 5 has no children";
|
|
||||||
// after
|
|
||||||
if(!DataObject::get('SiteTree', '"ParentID" = 5')->count()) echo "Page 5 has no children";
|
|
||||||
|
|
||||||
See the ["datamodel" documentation](../../topics/datamodel) for more details.
|
|
||||||
|
|
||||||
### New ORM: Changes to manipulation of SQL queries ###
|
|
||||||
|
|
||||||
In the 2.4 ORM it was sometimes necessary to bypass the ORM for performance reasons. For example,
|
|
||||||
this command would have been intolerably slow:
|
|
||||||
|
|
||||||
DataList::create('SiteTree')->count();
|
|
||||||
|
|
||||||
The 3.0 ORM is more intelligent gives you tools you need to create high-performance code without
|
|
||||||
bypassing the ORM:
|
|
||||||
|
|
||||||
// before
|
|
||||||
echo DB::query("SELECT COUNT(*) FROM \"SiteTree\"")->value();
|
|
||||||
// after
|
|
||||||
echo DataList::create('SiteTree')->count()
|
|
||||||
|
|
||||||
Both `extendedSQL()` and `buildSQL()` have been deprecated. There is not currently any way of
|
|
||||||
overriding the query generation code equivalent to overriding `buildSQL()` in 2.4, but this facility
|
|
||||||
was error prone. If you need to access the `SQLQuery` object, you can call `->dataQuery()->query()`
|
|
||||||
on any DataList. Note that modifications to this query will **not** be passed back into the DataList.
|
|
||||||
|
|
||||||
// before
|
|
||||||
$query = singleton('SiteTree')->extendedSQL('ParentID = 5');
|
|
||||||
// after
|
|
||||||
$query = DataList::create('SiteTree')->filter('ParentID', 5)->dataQuery()->query();
|
|
||||||
|
|
||||||
We advise that you keep this kind of code to a minimum and that you use the DataList wherever possible.
|
|
||||||
If you find yourself needing to bypass the ORM in SilverStripe 3, we suggest you raise this
|
|
||||||
as a discussion topic on silverstripe-dev@groups.google.com, as we may want to add more tools to
|
|
||||||
the ORM to help you.
|
|
||||||
|
|
||||||
### New ORM: Better encapsulation of relationship queries with `RelationList` ###
|
|
||||||
|
|
||||||
The abstract `RelationList` class and its implementations `ManyManyList` and `HasManyList`
|
|
||||||
are replacing the `ComponentSet` API, which is only relevant if you have instanciated these manually.
|
|
||||||
Relations are retrieved through the same way (e.g. `$myMember->Groups()`).
|
|
||||||
|
|
||||||
### InnoDB driver for existing and new tables on MySQL (instead of MyISAM) ###
|
|
||||||
|
|
||||||
SilverStripe has traditionally created all MySQL tables with the MyISAM storage driver,
|
|
||||||
mainly to ensure a fulltext search based on MySQL works out of the box.
|
|
||||||
Since then, the framework has gained many alternatives for fulltext search
|
|
||||||
([sphinx](https://github.com/silverstripe/silverstripe-sphinx), [solr](https://github.com/nyeholt/silverstripe-solr), etc.), and relies more on database transactions and other features not available in MyISAM.
|
|
||||||
|
|
||||||
This change convert tables on existing databases when `dev/build` is called,
|
|
||||||
unless the `FullTextSearch` feature is enabled. In order to disable this behaviour,
|
|
||||||
you have to add the following code to your `_config.php` BEFORE running a `dev/build`:
|
|
||||||
|
|
||||||
DataObject::$create_table_options['MySQLDatabase] = 'ENGINE=MyISAM';
|
|
||||||
|
|
||||||
As with any SilverStripe upgrade, we recommend database backups before calling `dev/build`.
|
|
||||||
See [mysql.com](http://dev.mysql.com/doc/refman/5.5/en/converting-tables-to-innodb.html) for details on the conversion.
|
|
||||||
Note: MySQL has made InnoDB the default engine in its [5.5 release](http://dev.mysql.com/doc/refman/5.5/en/innodb-storage-engine.html).
|
|
||||||
|
|
||||||
### Deprecation API ###
|
|
||||||
|
|
||||||
There is a new deprecation API that generates deprecation notices. Calls to Deprecated methods
|
|
||||||
will only produce errors if the API was deprecated in the release equal to or earlier than the
|
|
||||||
"notification version".
|
|
||||||
|
|
||||||
`sapphire/_config.php` currently contains a call to throw notices calls to all methods deprecated
|
|
||||||
in 3.0.
|
|
||||||
|
|
||||||
Deprecation::notification_version('3.0.0');
|
|
||||||
|
|
||||||
If you change the notification version to 3.0.0-dev, then only methods deprecated in older versions
|
|
||||||
(e.g. 2.4) will trigger notices, and the other methods will silently pass. This can be useful if
|
|
||||||
you don't yet have time to remove all calls to deprecated methods.
|
|
||||||
|
|
||||||
Deprecation::notification_version('3.0.0-dev');
|
|
||||||
|
|
||||||
### Deprecated Classes and Methods ###
|
|
||||||
|
|
||||||
* `FieldSet`: Use `FieldList` instead
|
|
||||||
* `DataObjectSet`: Use `ArrayList` or `DataList` instead
|
|
||||||
* `SQLMap`: Use `SS_Map` instead
|
|
||||||
* `ComponentSet`: Use `ManyManyList` or `HasManyList`
|
|
||||||
|
|
||||||
See [3.0.0 upgrading guide](../3.0.0) for further details.
|
See [3.0.0 upgrading guide](../3.0.0) for further details.
|
||||||
|
|
||||||
## Changelog ##
|
## Changelog ##
|
||||||
|
Loading…
x
Reference in New Issue
Block a user