Update config documentation

This commit is contained in:
Damian Mooyman 2017-02-23 11:42:36 +13:00
parent 8444a21cbf
commit e2064b5cc8
5 changed files with 84 additions and 48 deletions

View File

@ -42,7 +42,7 @@ config:
:::yaml :::yaml
Injector: SilverStripe\Core\Injector\Injector:
DataListFilter.CustomMatch: DataListFilter.CustomMatch:
class: MyVendor/Search/CustomMatchFilter class: MyVendor/Search/CustomMatchFilter

View File

@ -23,7 +23,7 @@ also track versioned history.
:::php :::php
class MyStagedModel extends DataObject { class MyStagedModel extends DataObject {
private staic $extensions = [ private staic $extensions = [
"Versioned" Versioned::class
]; ];
} }
@ -34,8 +34,8 @@ can be specified by setting the constructor argument to "Versioned"
:::php :::php
class VersionedModel extends DataObject { class VersionedModel extends DataObject {
private staic $extensions = [ private static $extensions = [
"Versioned('Versioned')" "SilverStripe\\ORM\\Versioning\\Versioned('Versioned')"
]; ];
} }
@ -63,14 +63,14 @@ adding a suffix.
* `MyRecord` table: Contains staged data * `MyRecord` table: Contains staged data
* `MyRecord_Live` table: Contains live data * `MyRecord_Live` table: Contains live data
* `MyRecord_versions` table: Contains a version history (new record created on each save) * `MyRecord_Versions` table: Contains a version history (new record created on each save)
Similarly, any subclass you create on top of a versioned base will trigger the creation of additional tables, which are Similarly, any subclass you create on top of a versioned base will trigger the creation of additional tables, which are
automatically joined as required: automatically joined as required:
* `MyRecordSubclass` table: Contains only staged data for subclass columns * `MyRecordSubclass` table: Contains only staged data for subclass columns
* `MyRecordSubclass_Live` table: Contains only live data for subclass columns * `MyRecordSubclass_Live` table: Contains only live data for subclass columns
* `MyRecordSubclass_versions` table: Contains only version history for subclass columns * `MyRecordSubclass_Versions` table: Contains only version history for subclass columns
## Usage ## Usage
@ -81,12 +81,12 @@ explicitly request a certain stage through various getters on the `Versioned` cl
:::php :::php
// Fetching multiple records // Fetching multiple records
$stageRecords = Versioned::get_by_stage('MyRecord', 'Stage'); $stageRecords = Versioned::get_by_stage('MyRecord', Versioned::DRAFT);
$liveRecords = Versioned::get_by_stage('MyRecord', 'Live'); $liveRecords = Versioned::get_by_stage('MyRecord', Versioned::LIVE);
// Fetching a single record // Fetching a single record
$stageRecord = Versioned::get_by_stage('MyRecord', 'Stage')->byID(99); $stageRecord = Versioned::get_by_stage('MyRecord', Versioned::DRAFT)->byID(99);
$liveRecord = Versioned::get_by_stage('MyRecord', 'Live')->byID(99); $liveRecord = Versioned::get_by_stage('MyRecord', Versioned::LIVE)->byID(99);
### Historical Versions ### Historical Versions
@ -131,7 +131,7 @@ done via one of several ways:
See "DataObject ownership" for reference on dependant objects. See "DataObject ownership" for reference on dependant objects.
:::php :::php
$record = Versioned::get_by_stage('MyRecord', 'Stage')->byID(99); $record = Versioned::get_by_stage('MyRecord', Versioned::DRAFT)->byID(99);
$record->MyField = 'changed'; $record->MyField = 'changed';
// will update `MyRecord` table (assuming Versioned::current_stage() == 'Stage'), // will update `MyRecord` table (assuming Versioned::current_stage() == 'Stage'),
// and write a row to `MyRecord_versions`. // and write a row to `MyRecord_versions`.
@ -144,7 +144,7 @@ Similarly, an "unpublish" operation does the reverse, and removes a record from
:::php :::php
$record = MyRecord::get()->byID(99); // stage doesn't matter here $record = MyRecord::get()->byID(99); // stage doesn't matter here
// will remove the row from the `MyRecord_Live` table // will remove the row from the `MyRecord_Live` table
$record->deleteFromStage('Live'); $record->deleteFromStage(Versioned::LIVE);
### Forcing the Current Stage ### Forcing the Current Stage
@ -154,7 +154,7 @@ is initialized. But it can also be set and reset temporarily to force a specific
:::php :::php
$origMode = Versioned::get_reading_mode(); // save current mode $origMode = Versioned::get_reading_mode(); // save current mode
$obj = MyRecord::getComplexObjectRetrieval(); // returns 'Live' records $obj = MyRecord::getComplexObjectRetrieval(); // returns 'Live' records
Versioned::set_reading_mode('Stage'); // temporarily overwrite mode Versioned::set_reading_mode(Versioned::DRAFT); // temporarily overwrite mode
$obj = MyRecord::getComplexObjectRetrieval(); // returns 'Stage' records $obj = MyRecord::getComplexObjectRetrieval(); // returns 'Stage' records
Versioned::set_reading_mode($origMode); // reset current mode Versioned::set_reading_mode($origMode); // reset current mode
@ -180,7 +180,7 @@ without requiring any custom code.
:::php :::php
class MyPage extends Page { class MyPage extends Page {
private static $has_many = array( private static $has_many = array(
'Banners' => 'Banner' 'Banners' => Banner::class
); );
private static $owns = array( private static $owns = array(
'Banners' 'Banners'
@ -189,11 +189,11 @@ without requiring any custom code.
class Banner extends Page { class Banner extends Page {
private static $extensions = array( private static $extensions = array(
'Versioned' Versioned::class
); );
private static $has_one = array( private static $has_one = array(
'Parent' => 'MyPage', 'Parent' => MyPage::class,
'Image' => 'Image', 'Image' => Image::class,
); );
private static $owns = array( private static $owns = array(
'Image' 'Image'
@ -219,7 +219,7 @@ E.g.
:::php :::php
class MyParent extends DataObject { class MyParent extends DataObject {
private static $extensions = array( private static $extensions = array(
'Versioned' Versioned::class
); );
private static $owns = array( private static $owns = array(
'ChildObjects' 'ChildObjects'
@ -230,7 +230,7 @@ E.g.
} }
class MyChild extends DataObject { class MyChild extends DataObject {
private static $extensions = array( private static $extensions = array(
'Versioned' Versioned::class
); );
private static $owned_by = array( private static $owned_by = array(
'Parent' 'Parent'
@ -258,7 +258,7 @@ smaller modifications of the generated `DataList` objects.
Example: Get the first 10 live records, filtered by creation date: Example: Get the first 10 live records, filtered by creation date:
:::php :::php
$records = Versioned::get_by_stage('MyRecord', 'Live')->limit(10)->sort('Created', 'ASC'); $records = Versioned::get_by_stage('MyRecord', Versioned::LIVE)->limit(10)->sort('Created', 'ASC');
### Permissions ### Permissions
@ -283,7 +283,7 @@ E.g.
:::php :::php
class MyObject extends DataObject { class MyObject extends DataObject {
private static $extensions = array( private static $extensions = array(
'Versioned' Versioned::class,
); );
public function canViewVersioned($member = null) { public function canViewVersioned($member = null) {
@ -325,7 +325,7 @@ E.g.
:::php :::php
class MyObject extends DataObject { class MyObject extends DataObject {
private static $extensions = array( private static $extensions = array(
'Versioned' Versioned::class,
); );
private static $non_live_permissions = array('ADMIN'); private static $non_live_permissions = array('ADMIN');
} }
@ -348,7 +348,7 @@ to force a specific stage, we recommend the `Controller->init()` method for this
:::php :::php
public function init() { public function init() {
parent::init(); parent::init();
Versioned::set_reading_mode('Stage.Stage'); Versioned::set_stage(Versioned::DRAFT);
} }

View File

@ -49,7 +49,7 @@ Make sure that after you have modified the `routes.yml` file, that you clear you
Name: mysiteroutes Name: mysiteroutes
After: framework/routes#coreroutes After: framework/routes#coreroutes
--- ---
Director: SilverStripe\Control\Director:
rules: rules:
'teams//$Action/$ID/$Name': 'TeamController' 'teams//$Action/$ID/$Name': 'TeamController'

View File

@ -5,7 +5,8 @@ summary: SilverStripe's YAML based Configuration API for setting runtime configu
SilverStripe comes with a comprehensive code based configuration system through the [api:Config] class. It primarily SilverStripe comes with a comprehensive code based configuration system through the [api:Config] class. It primarily
relies on declarative [YAML](http://en.wikipedia.org/wiki/YAML) files, and falls back to procedural PHP code, as well relies on declarative [YAML](http://en.wikipedia.org/wiki/YAML) files, and falls back to procedural PHP code, as well
as PHP static variables. as PHP static variables. This is provided by the [silverstripe/config](https://github.com/silverstripe/silverstripe-config)
library.
The Configuration API can be seen as separate from other forms of variables in the SilverStripe system due to three The Configuration API can be seen as separate from other forms of variables in the SilverStripe system due to three
properties API: properties API:
@ -49,19 +50,28 @@ be marked `private static` and follow the `lower_case_with_underscores` structur
This can be done by calling the static method [api:Config::inst()], like so: This can be done by calling the static method [api:Config::inst()], like so:
:::php :::php
$config = Config::inst()->get('MyClass'); $config = Config::inst()->get('MyClass', 'property');
Or through the `config()` object on the class. Or through the `config()` object on the class.
$config = $this->config(); $config = $this->config()->get('property')';
There are three public methods available on the instance. `get($class, $variable)`, `remove($class, $variable)` and Note that by default `Config::inst()` returns only an immutable version of config. Use `Config::modify()`
`update($class, $variable, $value)`. if it's necessary to alter class config. This is generally undesirable in most applications, as modification
of the config can immediately have performance implications, so this should be used sparingly, or
during testing to modify state.
<div class="notice" markdown="1"> Note that while both objects have similar methods the APIs differ slightly. The below actions are equivalent:
There is no "set" method. It is not possible to completely set the value of a classes' property. `update` adds new
values that are treated as the highest priority in the merge, and remove adds a merge mask that filters out values. * `Config::inst()->get('Class', 'property');` or `Class::config()->get('property')`
</div> * `Config::inst()->uninherited('Class', 'property');` or `Class::config()->get('property', Config::UNINHERITED)`
* `Config::inst()->exists('Class', 'property');` or `Class::config()->exists('property')`
And mutable methods:
* `Config::modify()->merge('Class', 'property', 'newvalue');` or `Class::config()->merge('property', 'newvalue')`
* `Config::modify()->set('Class', 'property', 'newvalue');` or `Class::config()->set('property', 'newvalue')`
* `Config::modify()->remove('Class', 'property');` or `Class::config()->remove('property')`
To set those configuration options on our previously defined class we can define it in a `YAML` file. To set those configuration options on our previously defined class we can define it in a `YAML` file.
@ -92,7 +102,7 @@ To use those variables in your application code:
echo implode(', ', Config::inst()->get('MyClass', 'option_two')); echo implode(', ', Config::inst()->get('MyClass', 'option_two'));
// returns 'Foo, Bar, Baz' // returns 'Foo, Bar, Baz'
Config::inst()->update('MyClass', 'option_one', true); Config::inst()->set('MyClass', 'option_one', true);
echo Config::inst()->get('MyClass', 'option_one'); echo Config::inst()->get('MyClass', 'option_one');
// returns true // returns true
@ -134,7 +144,7 @@ the result will be the higher priority false-ish value.
The locations that configuration values are taken from in highest -> lowest priority order are: The locations that configuration values are taken from in highest -> lowest priority order are:
- Any values set via a call to Config#update - Any values set via a call to Config#merge / Config#set
- The configuration values taken from the YAML files in `_config/` directories (internally sorted in before / after - The configuration values taken from the YAML files in `_config/` directories (internally sorted in before / after
order, where the item that is latest is highest priority) order, where the item that is latest is highest priority)
- Any static set on an "additional static source" class (such as an extension) named the same as the name of the property - Any static set on an "additional static source" class (such as an extension) named the same as the name of the property
@ -155,13 +165,13 @@ rather than add.
'allowed_actions', Config::UNINHERITED 'allowed_actions', Config::UNINHERITED
); );
They are much simpler. They consist of a list of key / value pairs. When applied against the current composite value Available masks include:
- If the composite value is a sequential array, any member of that array that matches any value in the mask is removed * Config::UNINHERITED - Exclude config inherited from parent classes
- If the composite value is an associative array, any member of that array that matches both the key and value of any * Config::EXCLUDE_EXTRA_SOURCES - Exclude config applied by extensions
pair in the mask is removed
- If the composite value is not an array, if that value matches any value in the mask it is removed
You can also pass in literal `true` to disable all extra sources, or merge config options with
bitwise `|` operator.
## Configuration YAML Syntax and Rules ## Configuration YAML Syntax and Rules
@ -280,11 +290,13 @@ rules contained match.
You then list any of the following rules as sub-keys, with informational values as either a single value or a list. You then list any of the following rules as sub-keys, with informational values as either a single value or a list.
- 'classexists', in which case the value(s) should be classes that must exist - 'classexists', in which case the value(s) should be classes that must exist
- 'moduleexists', in which case the value(s) should be modules that must exist - 'moduleexists', in which case the value(s) should be modules that must exist. This supports either folder
name or composer `vendor/name` format.
- 'environment', in which case the value(s) should be one of "live", "test" or "dev" to indicate the SilverStripe - 'environment', in which case the value(s) should be one of "live", "test" or "dev" to indicate the SilverStripe
mode the site must be in mode the site must be in
- 'envvarset', in which case the value(s) should be environment variables that must be set - 'envvarset', in which case the value(s) should be environment variables that must be set
- 'constantdefined', in which case the value(s) should be constants that must be defined - 'constantdefined', in which case the value(s) should be constants that must be defined
- 'envorconstant' A variable which should be defined either via environment vars or constants
For instance, to add a property to "foo" when a module exists, and "bar" otherwise, you could do this: For instance, to add a property to "foo" when a module exists, and "bar" otherwise, you could do this:
@ -303,19 +315,23 @@ For instance, to add a property to "foo" when a module exists, and "bar" otherwi
property: 'bar' property: 'bar'
--- ---
Multiple conditions of the same type can be declared via array format
:::yaml
---
Only:
moduleexists:
- 'silverstripe/blog'
- 'silverstripe/lumberjack'
<div class="alert" markdown="1"> <div class="alert" markdown="1">
When you have more than one rule for a nested fragment, they're joined like When you have more than one rule for a nested fragment, they're joined like
`FRAGMENT_INCLUDED = (ONLY && ONLY) && !(EXCEPT && EXCEPT)`. `FRAGMENT_INCLUDED = (ONLY && ONLY) && !(EXCEPT && EXCEPT)`.
That is, the fragment will be included if all Only rules match, except if all Except rules match. That is, the fragment will be included if all Only rules match, except if all Except rules match.
</div> </div>
<div class="alert" markdown="1">
Due to YAML limitations, having multiple conditions of the same kind (say, two `EnvVarSet` in one "Only" block)
will result in only the latter coming through.
</div>
## API Documentation ## API Documentation
* [api:Config] * [api:Config]

View File

@ -959,6 +959,26 @@ to update those with the appropriate function or config call. See
[CMS architecture](/developer_guides/customising_the_admin_interface/cms-architecture#the-admin-url) for language [CMS architecture](/developer_guides/customising_the_admin_interface/cms-architecture#the-admin-url) for language
specific functions. specific functions.
#### Upgrading Config API usages
Performance optimisations have been made to Config which, under certain circumstances, require developer
care when modifying or caching config values. The top level config object is now immutable on application
bootstrap, and requires a developer to invoke `Config::modify()` to make mutable prior to modification.
This will immediately have a slight performance hit, so should be done sparingly, and avoided at all
if possible in performance intensive applications.
In addition, the `Config::inst()->update()` method is deprecated, and replaced with `Config::modify()->set()` and
`Config::modify()->merge()` to respectively replace and merge config.
When config is merged (either via modification or merged between yml blocks) falsey-values (including nulls)
now replace any prior values (even arrays).
One removed feature is the `Config::FIRST_SET` option. Either use uninherited config directly on the class
directly, or use the inherited config lookup. As falsey values now overwrite all parent class values, it is
now generally safer to use the default inherited config, where in the past you would need to use `FIRST_SET`.
## <a name="api-changes"></a>API Changes ## <a name="api-changes"></a>API Changes
### <a name="overview-general"></a>General and Core API ### <a name="overview-general"></a>General and Core API