DOCS Update requirements and error handling, fix typos and markdown rendering bugs

This commit is contained in:
Robbie Averill 2017-07-10 11:00:25 +12:00
parent 0b09a510f4
commit ee0bd1a541
2 changed files with 254 additions and 233 deletions

View File

@ -29,44 +29,53 @@ Requiring assets from the template is restricted compared to the PHP API.
It is common practice to include most Requirements either in the *init()*-method of your [controller](../controllers/), or It is common practice to include most Requirements either in the *init()*-method of your [controller](../controllers/), or
as close to rendering as possible (e.g. in [FormField](api:SilverStripe\Forms\FormField)). as close to rendering as possible (e.g. in [FormField](api:SilverStripe\Forms\FormField)).
:::php ```php
<?php <?php
class MyCustomController extends Controller { use SilverStripe\Control\Director;
use SilverStripe\View\Requirements;
public function init() { class MyCustomController extends Controller
{
protected function init()
{
parent::init(); parent::init();
Requirements::javascript("<my-module-dir>/javascript/some_file.js"); Requirements::javascript("<my-module-dir>/javascript/some_file.js");
Requirements::css("<my-module-dir>/css/some_file.css"); Requirements::css("<my-module-dir>/css/some_file.css");
} }
} }
```
### CSS Files ### CSS Files
:::php ```php
Requirements::css($path, $media); Requirements::css($path, $media);
```
If you're using the CSS method a second argument can be used. This argument defines the 'media' attribute of the If you're using the CSS method a second argument can be used. This argument defines the 'media' attribute of the
`<link>` element, so you can define 'screen' or 'print' for example. `<link>` element, so you can define 'screen' or 'print' for example.
:::php ```php
Requirements::css("<my-module-dir>/css/some_file.css", "screen,projection"); Requirements::css("<my-module-dir>/css/some_file.css", "screen,projection");
```
### Javascript Files ### Javascript Files
:::php ```php
Requirements::javascript($path, $options); Requirements::javascript($path, $options);
```
A variant on the inclusion of custom javascript is the inclusion of *templated* javascript. Here, you keep your A variant on the inclusion of custom javascript is the inclusion of *templated* javascript. Here, you keep your
JavaScript in a separate file and instead load, via search and replace, several PHP-generated variables into that code. JavaScript in a separate file and instead load, via search and replace, several PHP-generated variables into that code.
:::php ```php
$vars = array( $vars = [
"MemberID" => Member::currentUserID(), "MemberID" => Member::currentUserID(),
); ];
Requirements::javascriptTemplate("<my-module-dir>/javascript/some_file.js", $vars); Requirements::javascriptTemplate("<my-module-dir>/javascript/some_file.js", $vars);
```
In this example, `some_file.js` is expected to contain a replaceable variable expressed as `MemberID`. In this example, `some_file.js` is expected to contain a replaceable variable expressed as `MemberID`.
@ -75,24 +84,26 @@ that your included files provide these scripts. This will ensure that subsequent
Requirement calls that rely on those included scripts will not double include those Requirement calls that rely on those included scripts will not double include those
files. files.
:::php ```php
Requirements::javascript('<my-module-dir>/javascript/dist/bundle.js', ['provides' => [ Requirements::javascript('<my-module-dir>/javascript/dist/bundle.js', ['provides' => [
'<my-module-dir>/javascript/jquery.js' '<my-module-dir>/javascript/jquery.js'
'<my-module-dir>/javascript/src/main.js', '<my-module-dir>/javascript/src/main.js',
'<my-module-dir>/javascript/src/functions.js' '<my-module-dir>/javascript/src/functions.js'
]]); ]]);
Requirements::javascript('<my-module-dir>/javascript/jquery.js'); // Will will skip this file Requirements::javascript('<my-module-dir>/javascript/jquery.js'); // Will will skip this file
```
You can also use the second argumet to add the 'async' and/or 'defer attributes to the script tag generated: You can also use the second argument to add the 'async' and/or 'defer attributes to the script tag generated:
:::php ```php
Requirements::javascript( Requirements::javascript(
"<my-module-dir>/javascript/some_file.js", "<my-module-dir>/javascript/some_file.js",
array( [
"async" => true, "async" => true,
"defer" => true, "defer" => true,
) ]
); );
```
### Custom Inline CSS or Javascript ### Custom Inline CSS or Javascript
@ -101,32 +112,34 @@ of 'configuration' from the database in a raw format. You'll need to use the `h
this is generally speaking the best way to do these things - it clearly marks the copy as belonging to a different this is generally speaking the best way to do these things - it clearly marks the copy as belonging to a different
language. language.
:::php ```php
Requirements::customScript(<<<JS Requirements::customScript(<<<JS
alert("hi there"); alert("hi there");
JS JS
); );
Requirements::customCSS(<<<CSS Requirements::customCSS(<<<CSS
.tree li.$className { .tree li.$className {
background-image: url($icon); background-image: url($icon);
} }
CSS CSS
); );
```
## Combining Files ## Combining Files
You can concatenate several CSS or javascript files into a single dynamically generated file. This increases performance You can concatenate several CSS or javascript files into a single dynamically generated file. This increases performance
by reducing HTTP requests. by reducing HTTP requests.
:::php ```php
Requirements::combine_files( Requirements::combine_files(
'foobar.js', 'foobar.js',
array( [
'<my-module-dir>/javascript/foo.js', '<my-module-dir>/javascript/foo.js',
'<my-module-dir>/javascript/bar.js', '<my-module-dir>/javascript/bar.js',
) ]
); );
```
<div class="alert" markdown='1'> <div class="alert" markdown='1'>
To make debugging easier in your local environment, combined files is disabled when running your application in `dev` To make debugging easier in your local environment, combined files is disabled when running your application in `dev`
@ -165,17 +178,17 @@ is not appropriate. Normally a single backend is used for all site assets, so a
replaced. For instance, the below will set a new set of dependencies to write to `mysite/javascript/combined` replaced. For instance, the below will set a new set of dependencies to write to `mysite/javascript/combined`
:::yaml ```yaml
--- ---
Name: myrequirements Name: myrequirements
--- ---
SilverStripe\View\Requirements: SilverStripe\View\Requirements:
disable_flush_combined: true disable_flush_combined: true
SilverStripe\View\Requirements_Backend: SilverStripe\View\Requirements_Backend:
combine_in_dev: true combine_in_dev: true
combine_hash_querystring: true combine_hash_querystring: true
default_combined_files_folder: 'combined' default_combined_files_folder: 'combined'
SilverStripe\Core\Injector\Injector: SilverStripe\Core\Injector\Injector:
# Create adapter that points to the custom directory root # Create adapter that points to the custom directory root
SilverStripe\Assets\Flysystem\PublicAdapter.custom-adapter: SilverStripe\Assets\Flysystem\PublicAdapter.custom-adapter:
class: SilverStripe\Assets\Flysystem\PublicAssetAdapter class: SilverStripe\Assets\Flysystem\PublicAssetAdapter
@ -195,6 +208,7 @@ replaced. For instance, the below will set a new set of dependencies to write to
SilverStripe\View\Requirements_Backend: SilverStripe\View\Requirements_Backend:
properties: properties:
AssetHandler: '%$SilverStripe\Assets\Storage\GeneratedAssetHandler.custom-generated-assets' AssetHandler: '%$SilverStripe\Assets\Storage\GeneratedAssetHandler.custom-generated-assets'
```
In the above configuration, automatic expiry of generated files has been disabled, and it is necessary for In the above configuration, automatic expiry of generated files has been disabled, and it is necessary for
the developer to maintain these files manually. This may be useful in environments where assets must the developer to maintain these files manually. This may be useful in environments where assets must
@ -215,10 +229,10 @@ the third paramter of the `combine_files` function:
$loader = SilverStripe\View\ThemeResourceLoader::inst(); $loader = SilverStripe\View\ThemeResourceLoader::inst();
$themes = SilverStripe\View\SSViewer::get_themes(); $themes = SilverStripe\View\SSViewer::get_themes();
$printStylesheets = array( $printStylesheets = [
$loader->findThemedCSS('print_HomePage.css', $themes), $loader->findThemedCSS('print_HomePage.css', $themes),
$loader->findThemedCSS('print_Page.css', $themes) $loader->findThemedCSS('print_Page.css', $themes)
); ];
SilverStripe\View\Requirements::combine_files('print.css', $printStylesheets, 'print'); SilverStripe\View\Requirements::combine_files('print.css', $printStylesheets, 'print');
``` ```
@ -240,12 +254,12 @@ You can also add the 'async' and/or 'defer' attributes to combined Javascript fi
$loader = SilverStripe\View\ThemeResourceLoader::inst(); $loader = SilverStripe\View\ThemeResourceLoader::inst();
$themes = SilverStripe\View\SSViewer::get_themes(); $themes = SilverStripe\View\SSViewer::get_themes();
$scripts = array( $scripts = [
$loader->findThemedJavascript('some_script.js', $themes), $loader->findThemedJavascript('some_script.js', $themes),
$loader->findThemedJavascript('some_other_script.js', $themes) $loader->findThemedJavascript('some_other_script.js', $themes)
); ];
SilverStripe\View\Requirements::combine_files('scripts.js', $scripts, array('async' => true, 'defer' => true)); SilverStripe\View\Requirements::combine_files('scripts.js', $scripts, ['async' => true, 'defer' => true]);
``` ```
### Minification of CSS and JS files ### Minification of CSS and JS files
@ -294,13 +308,15 @@ tools to do this for you, e.g. [Webpack](https://webpack.github.io/), [Gulp](htt
## Clearing assets ## Clearing assets
:::php ```php
Requirements::clear(); Requirements::clear();
```
Clears all defined requirements. You can also clear specific requirements. Clears all defined requirements. You can also clear specific requirements.
:::php ```php
Requirements::clear(THIRDPARTY_DIR.'/prototype.js'); Requirements::clear('modulename/javascript/some-lib.js');
```
<div class="alert" markdown="1"> <div class="alert" markdown="1">
Depending on where you call this command, a Requirement might be *re-included* afterwards. Depending on where you call this command, a Requirement might be *re-included* afterwards.
@ -315,8 +331,9 @@ included requirements, and ones included after the `block()` call.
One common example is to block the core `jquery.js` added by various form fields and core controllers, and use a newer One common example is to block the core `jquery.js` added by various form fields and core controllers, and use a newer
version in a custom location. This assumes you have tested your application with the newer version. version in a custom location. This assumes you have tested your application with the newer version.
:::php ```php
Requirements::block(ADMIN_THIRDPARTY_DIR . '/jquery/jquery.js'); Requirements::block('silverstripe-admin/thirdparty/jquery/jquery.js');
```
<div class="alert" markdown="1"> <div class="alert" markdown="1">
The CMS also uses the `Requirements` system, and its operation can be affected by `block()` calls. Avoid this by The CMS also uses the `Requirements` system, and its operation can be affected by `block()` calls. Avoid this by
@ -338,8 +355,9 @@ careful when messing with the order of requirements.
By default, SilverStripe includes all Javascript files at the bottom of the page body, unless there's another script By default, SilverStripe includes all Javascript files at the bottom of the page body, unless there's another script
already loaded, then, it's inserted before the first `<script>` tag. If this causes problems, it can be configured. already loaded, then, it's inserted before the first `<script>` tag. If this causes problems, it can be configured.
:::php ```php
Requirements::set_force_js_to_bottom(true); Requirements::set_force_js_to_bottom(true);
```
`Requirements.force_js_to_bottom`, will force SilverStripe to write the Javascript to the bottom of the page body, even `Requirements.force_js_to_bottom`, will force SilverStripe to write the Javascript to the bottom of the page body, even
if there is an earlier script tag. if there is an earlier script tag.
@ -347,8 +365,9 @@ if there is an earlier script tag.
If the Javascript files are preferred to be placed in the `<head>` tag rather than in the `<body>` tag, If the Javascript files are preferred to be placed in the `<head>` tag rather than in the `<body>` tag,
`Requirements.write_js_to_body` should be set to false. `Requirements.write_js_to_body` should be set to false.
:::php ```php
Requirements::set_write_js_to_body(false); Requirements::set_write_js_to_body(false);
```
## API Documentation ## API Documentation

View File

@ -13,9 +13,10 @@ warning of a potential issue to handle.
For informational and debug logs, you can use the Logger directly. The Logger is a PSR-3 compatible LoggerInterface and For informational and debug logs, you can use the Logger directly. The Logger is a PSR-3 compatible LoggerInterface and
can be accessed via the `Injector`: can be accessed via the `Injector`:
:::php ```php
Injector::inst()->get(LoggerInterface::class)->info('User has logged in: ID #' . Member::currentUserID()); Injector::inst()->get(LoggerInterface::class)->info('User has logged in: ID #' . Member::currentUserID());
Injector::inst()->get(LoggerInterface::class)->debug('Query executed: ' . $sql); Injector::inst()->get(LoggerInterface::class)->debug('Query executed: ' . $sql);
```
Although you can raise more important levels of alerts in this way, we recommend using PHP's native error systems for Although you can raise more important levels of alerts in this way, we recommend using PHP's native error systems for
these instead. these instead.
@ -23,28 +24,32 @@ these instead.
For notice-level and warning-level issues, you should use [user_error](http://www.php.net/user_error) to throw errors For notice-level and warning-level issues, you should use [user_error](http://www.php.net/user_error) to throw errors
where appropriate. These will not halt execution but will send a message to the where appropriate. These will not halt execution but will send a message to the
:::php ```php
function delete() { public function delete()
if($this->alreadyDelete) { {
if ($this->alreadyDelete) {
user_error("Delete called on already deleted object", E_USER_NOTICE); user_error("Delete called on already deleted object", E_USER_NOTICE);
return; return;
} }
... // ...
} }
function getRelatedObject() { public function getRelatedObject()
if(!$this->RelatedObjectID) { {
if (!$this->RelatedObjectID) {
user_error("Can't find a related object", E_USER_WARNING); user_error("Can't find a related object", E_USER_WARNING);
return null; return;
}
...
} }
// ...
}
```
For errors that should halt execution, you should use Exceptions. Normally, Exceptions will halt the flow of executuion, For errors that should halt execution, you should use Exceptions. Normally, Exceptions will halt the flow of executuion,
but they can be caught with a try/catch clause. but they can be caught with a try/catch clause.
:::php ```php
throw new \LogicException("Query failed: " . $sql); throw new \LogicException("Query failed: " . $sql);
```
### Accessing the logger via dependency injection. ### Accessing the logger via dependency injection.
@ -53,22 +58,23 @@ it also means that you're coupling your code to global state, which is a bad des
approach is to use depedency injection to pass the logger in for you. The [Injector](../extending/Injector) approach is to use depedency injection to pass the logger in for you. The [Injector](../extending/Injector)
can help with this. The most straightforward is to specify a `dependencies` config setting, like this: can help with this. The most straightforward is to specify a `dependencies` config setting, like this:
:::php ```php
class MyController { class MyController extends Controller
{
private static $dependencies = array( private static $dependencies = [
'logger' => '%$Psr\Log\LoggerInterface', 'logger' => '%$Psr\Log\LoggerInterface',
); ];
// This will be set automatically, as long as MyController is instantiated via Injector // This will be set automatically, as long as MyController is instantiated via Injector
public $logger; public $logger;
function init() { protected function init()
{
$this->logger->debug("MyController::init() called"); $this->logger->debug("MyController::init() called");
parent::init(); parent::init();
} }
}
} ```
In other contexts, such as testing or batch processing, logger can be set to a different value by the code calling In other contexts, such as testing or batch processing, logger can be set to a different value by the code calling
MyController. MyController.
@ -94,7 +100,8 @@ for you to try.
To send emails, you can use Monolog's [NativeMailerHandler](https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/NativeMailerHandler.php#L74), like this: To send emails, you can use Monolog's [NativeMailerHandler](https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/NativeMailerHandler.php#L74), like this:
SilverStripe\Core\Injector\Injector: ```yaml
SilverStripe\Core\Injector\Injector:
Psr\Log\LoggerInterface: Psr\Log\LoggerInterface:
calls: calls:
MailHandler: [ pushHandler, [ %$MailHandler ] ] MailHandler: [ pushHandler, [ %$MailHandler ] ]
@ -108,6 +115,7 @@ To send emails, you can use Monolog's [NativeMailerHandler](https://github.com/S
properties: properties:
ContentType: text/html ContentType: text/html
Formatter: %$SilverStripe\Logging\DetailedErrorFormatter Formatter: %$SilverStripe\Logging\DetailedErrorFormatter
```
The first section 4 lines passes a new handler to `Logger::pushHandler()` from the named service `MailHandler`. The The first section 4 lines passes a new handler to `Logger::pushHandler()` from the named service `MailHandler`. The
next 10 lines define what the service is. next 10 lines define what the service is.
@ -119,7 +127,8 @@ The calls key, `MailHandler`, can be anything you like: its main purpose is to l
To log to a file, you can use Monolog's [StreamHandler](https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/StreamHandler.php#L74), like this: To log to a file, you can use Monolog's [StreamHandler](https://github.com/Seldaek/monolog/blob/master/src/Monolog/Handler/StreamHandler.php#L74), like this:
SilverStripe\Core\Injector\Injector: ```yaml
SilverStripe\Core\Injector\Injector:
Psr\Log\LoggerInterface: Psr\Log\LoggerInterface:
calls: calls:
LogFileHandler: [ pushHandler, [ %$LogFileHandler ] ] LogFileHandler: [ pushHandler, [ %$LogFileHandler ] ]
@ -128,6 +137,7 @@ To log to a file, you can use Monolog's [StreamHandler](https://github.com/Selda
constructor: constructor:
- "../silverstripe.log" - "../silverstripe.log"
- "info" - "info"
```
The log file will be relative to the framework/ path, so "../silverstripe.log" will create a file in your project root. The log file will be relative to the framework/ path, so "../silverstripe.log" will create a file in your project root.
@ -136,10 +146,12 @@ The log file will be relative to the framework/ path, so "../silverstripe.log" w
You can disable a handler by removing its pushHandlers call from the calls option of the Logger service definition. You can disable a handler by removing its pushHandlers call from the calls option of the Logger service definition.
The handler key of the default handler is `DisplayErrorHandler`, so you can disable it like this: The handler key of the default handler is `DisplayErrorHandler`, so you can disable it like this:
SilverStripe\Core\Injector\Injector: ```yaml
SilverStripe\Core\Injector\Injector:
Psr\Log\LoggerInterface: Psr\Log\LoggerInterface:
calls: calls:
DisplayErrorHandler: %%remove%% DisplayErrorHandler: %%remove%%
```
### Setting a different configuration for dev ### Setting a different configuration for dev
@ -147,12 +159,13 @@ In order to set different logging configuration on different environment types,
configuration features that the config system proviers. For example, here we have different configuration for dev and configuration features that the config system proviers. For example, here we have different configuration for dev and
non-dev. non-dev.
--- ```yaml
Name: dev-errors ---
Only: Name: dev-errors
Only:
environment: dev environment: dev
--- ---
SilverStripe\Core\Injector\Injector: SilverStripe\Core\Injector\Injector:
Psr\Log\LoggerInterface: Psr\Log\LoggerInterface:
calls: calls:
pushDisplayErrorHandler: [ pushHandler, [ %$DisplayErrorHandler ]] pushDisplayErrorHandler: [ pushHandler, [ %$DisplayErrorHandler ]]
@ -163,17 +176,17 @@ non-dev.
properties: properties:
Formatter: %$SilverStripe\Logging\DetailedErrorFormatter Formatter: %$SilverStripe\Logging\DetailedErrorFormatter
CLIFormatter: %$SilverStripe\Logging\DetailedErrorFormatter CLIFormatter: %$SilverStripe\Logging\DetailedErrorFormatter
--- ---
Name: live-errors Name: live-errors
Except: Except:
environment: dev environment: dev
--- ---
SilverStripe\Core\Injector\Injector: SilverStripe\Core\Injector\Injector:
Psr\Log\LoggerInterface: Psr\Log\LoggerInterface:
calls: calls:
pushFileLogHandler: [ pushHandler, [ %$LogFileHandler ]] pushFileLogHandler: [ pushHandler, [ %$LogFileHandler ]]
pushDisplayErrorHandler: [ pushHandler, [ %$DisplayErrorHandler ]] pushDisplayErrorHandler: [ pushHandler, [ %$DisplayErrorHandler ]]
LogFileHander: LogFileHandler:
class: Monolog\Handler\StreamHandler class: Monolog\Handler\StreamHandler
constructor: constructor:
- "../silverstripe.log" - "../silverstripe.log"
@ -192,6 +205,7 @@ non-dev.
properties: properties:
Title: "There has been an error" Title: "There has been an error"
Body: "The website server has not been able to respond to your request" Body: "The website server has not been able to respond to your request"
```
<div class="info" markdown="1"> <div class="info" markdown="1">
In addition to SilverStripe-integrated logging, it is advisable to fall back to PHPs native logging functionality. A In addition to SilverStripe-integrated logging, it is advisable to fall back to PHPs native logging functionality. A
@ -199,22 +213,6 @@ script might terminate before it reaches the SilverStripe error handling, for ex
sure `log_errors` and `error_log` in your PHP ini file are configured. sure `log_errors` and `error_log` in your PHP ini file are configured.
</div> </div>
## Email Logs
You can send both fatal errors and warnings in your code to a specified email-address.
**mysite/_config.php**
:::php
if(!Director::isDev()) {
// log errors and warnings
SS_Log::add_writer(new SS_LogFileWriter('../silverstripe-errors-warnings.log'), SS_Log::WARN, '<=');
// or just errors
SS_Log::add_writer(new SS_LogEmailWriter('admin@domain.com'), SS_Log::ERR);
}
## Replacing default implementations ## Replacing default implementations
For most application, Monolog and its default error handler should be fine, as you can get a lot of flexibility simply For most application, Monolog and its default error handler should be fine, as you can get a lot of flexibility simply
@ -226,11 +224,13 @@ others.
Monolog comes by default with SilverStripe, but you may use another PSR-3 compliant logger, if you wish. To do this, Monolog comes by default with SilverStripe, but you may use another PSR-3 compliant logger, if you wish. To do this,
set the `Injector.Logger` configuration parameter, providing a new injector definition. For example: set the `Injector.Logger` configuration parameter, providing a new injector definition. For example:
SilverStripe\Core\Injector\Injector: ```yaml
SilverStripe\Core\Injector\Injector:
ErrorHandler: ErrorHandler:
class: Logging\Logger class: Logging\Logger
constructor: constructor:
- 'alternative-logger' - 'alternative-logger'
```
If you do this, you will need to supply your own handlers, and the `Logger.handlers` configuration parameter will If you do this, you will need to supply your own handlers, and the `Logger.handlers` configuration parameter will
be ignored. be ignored.
@ -247,9 +247,11 @@ Core.php will call `start()` on this method, to start the error handler.
This error handler is flexible enough to work with any PSR-3 logging implementation, but sometimes you will want to use This error handler is flexible enough to work with any PSR-3 logging implementation, but sometimes you will want to use
another. To replace this, you should registered a new service, `ErrorHandlerLoader`. For example: another. To replace this, you should registered a new service, `ErrorHandlerLoader`. For example:
SilverStripe\Core\Injector\Injector: ```yaml
SilverStripe\Core\Injector\Injector:
ErrorHandler: ErrorHandler:
class: MyApp\CustomErrorHandlerLoader class: MyApp\CustomErrorHandlerLoader
```
You should register something with a `start()` method. You should register something with a `start()` method.