From 5fb6bab1249f23bb7f5701f48d1c52b30940e628 Mon Sep 17 00:00:00 2001 From: Web-VPF Date: Tue, 4 Jan 2022 18:10:37 +0200 Subject: [PATCH 01/30] Improving usability --- help-unit-testing.md | 116 +- help-using-composer.md | 108 +- markup-filter-app.md | 24 +- markup-filter-default.md | 14 +- markup-filter-image-height.md | 8 +- markup-filter-image-width.md | 8 +- markup-filter-md.md | 8 +- markup-filter-media.md | 8 +- markup-filter-page.md | 110 +- markup-filter-raw.md | 22 +- markup-filter-resize.md | 6 +- markup-filter-theme.md | 4 +- markup-function-dump.md | 12 +- markup-function-form.md | 92 +- markup-function-html.md | 36 +- markup-function-str.md | 54 +- markup-tag-component.md | 12 +- markup-tag-content.md | 60 +- markup-tag-flash.md | 62 +- markup-tag-for.md | 84 +- markup-tag-if.md | 62 +- markup-tag-macro.md | 106 +- markup-tag-page.md | 57 +- markup-tag-partial.md | 40 +- markup-tag-placeholder.md | 70 +- markup-tag-verbatim.md | 18 +- markup-templating.md | 52 +- markup-this-environment.md | 8 +- markup-this-layout.md | 8 +- markup-this-page.md | 32 +- markup-this-param.md | 26 +- markup-this-session.md | 26 +- markup-this-theme.md | 8 +- plugin-components.md | 454 +++--- plugin-extending.md | 358 ++--- plugin-localization.md | 48 +- plugin-registration.md | 22 +- plugin-scheduling.md | 94 +- plugin-settings.md | 177 +-- services-application.md | 108 +- services-asset-compilation.md | 15 +- services-collections.md | 2362 ++++++++++++++++++-------------- services-error-log.md | 154 ++- services-filesystem-cdn.md | 29 + services-hashing-encryption.md | 48 +- services-helpers.md | 513 ++++--- services-html.md | 170 ++- services-image-resizing.md | 8 + services-mail.md | 362 +++-- services-pagination.md | 120 +- services-parser.md | 274 ++-- services-queues.md | 46 +- services-request-input.md | 234 ++-- services-response-view.md | 190 ++- services-router.md | 186 ++- services-session.md | 54 +- services-validation.md | 420 +++--- setup-configuration.md | 275 ++-- setup-installation.md | 8 +- themes-development.md | 24 +- 60 files changed, 4843 insertions(+), 3271 deletions(-) diff --git a/help-unit-testing.md b/help-unit-testing.md index ddf01124..04cc81cd 100644 --- a/help-unit-testing.md +++ b/help-unit-testing.md @@ -12,82 +12,90 @@ Individual plugin test cases can be run by running [the `winter:test` command](. Plugins can be tested by creating a file called `phpunit.xml` in the base directory with the following content, for example, in a file **/plugins/acme/blog/phpunit.xml**: - - - - - ./tests - - - - - - - - +```xml + + + + + ./tests + + + + + + + + +``` Then a **tests/** directory can be created to contain the test classes. The file structure should mimic the base directory with classes having a `Test` suffix. Using a namespace for the class is also recommended. - 'Hi!']); - $this->assertEquals(1, $post->id); - } + $post = Post::create(['title' => 'Hi!']); + $this->assertEquals(1, $post->id); } +} +``` The test class should extend the base class `PluginTestCase` and this is a special class that will set up the Winter database stored in memory, as part of the `setUp` method. It will also refresh the plugin being tested, along with any of the defined dependencies in the plugin registration file. This is the equivalent of running the following before each test: - php artisan winter:up - php artisan plugin:refresh Acme.Blog - [php artisan plugin:refresh , ...] +```bash +php artisan winter:up +php artisan plugin:refresh Acme.Blog +[php artisan plugin:refresh , ...] +``` > **NOTE:** If your plugin uses [configuration files](../plugin/settings#file-configuration), then you will need to run `System\Classes\PluginManager::instance()->registerAll(true);` in the `setUp` method of your tests. Below is an example of a base test case class that should be used if you need to test your plugin working with other plugins instead of in isolation. - use System\Classes\PluginManager; +```php +use System\Classes\PluginManager; - class BaseTestCase extends PluginTestCase +class BaseTestCase extends PluginTestCase +{ + public function setUp(): void { - public function setUp(): void - { - parent::setUp(); + parent::setUp(); - // Get the plugin manager - $pluginManager = PluginManager::instance(); + // Get the plugin manager + $pluginManager = PluginManager::instance(); - // Register the plugins to make features like file configuration available - $pluginManager->registerAll(true); + // Register the plugins to make features like file configuration available + $pluginManager->registerAll(true); - // Boot all the plugins to test with dependencies of this plugin - $pluginManager->bootAll(true); - } + // Boot all the plugins to test with dependencies of this plugin + $pluginManager->bootAll(true); + } - public function tearDown(): void - { - parent::tearDown(); + public function tearDown(): void + { + parent::tearDown(); - // Get the plugin manager - $pluginManager = PluginManager::instance(); + // Get the plugin manager + $pluginManager = PluginManager::instance(); - // Ensure that plugins are registered again for the next test - $pluginManager->unregisterAll(); - } + // Ensure that plugins are registered again for the next test + $pluginManager->unregisterAll(); } +} +``` #### Changing database engine for plugins tests diff --git a/help-using-composer.md b/help-using-composer.md index ba4ae83d..6fdc9dd6 100644 --- a/help-using-composer.md +++ b/help-using-composer.md @@ -32,7 +32,7 @@ In order to use Composer with a Winter CMS instance that has been installed usin If you plan on submitting pull requests to the Winter CMS project via GitHub, or are actively developing a project based on Winter CMS and want to stay up to date with the absolute latest version, we recommend switching your composer dependencies to point to the `develop` branch where all the latest improvements and bug fixes take place. Doing this will allow you to catch any potential issues that may be introduced (as rare as they are) right when they happen and get them fixed while you're still actively working on your project instead of only discovering them several months down the road if they eventually make it into production. -``` +```json "winter/storm": "dev-develop as 1.1", "winter/wn-system-module": "dev-develop", "winter/wn-backend-module": "dev-develop", @@ -89,24 +89,26 @@ composer require --dev "" When publishing your plugins or themes to the marketplace, you may wish to also make them available via Composer. An example `composer.json` file for a plugin is included below: - { - "name": "winter/wn-demo-plugin", - "type": "winter-plugin", - "description": "Demo Winter CMS plugin", - "keywords": ["winter", "cms", "demo", "plugin"], - "license": "MIT", - "authors": [ - { - "name": "Winter CMS Maintainers", - "url": "https://wintercms.com", - "role": "Maintainer" - } - ], - "require": { - "php": ">=7.2", - "composer/installers": "~1.0" +```json +{ + "name": "winter/wn-demo-plugin", + "type": "winter-plugin", + "description": "Demo Winter CMS plugin", + "keywords": ["winter", "cms", "demo", "plugin"], + "license": "MIT", + "authors": [ + { + "name": "Winter CMS Maintainers", + "url": "https://wintercms.com", + "role": "Maintainer" } + ], + "require": { + "php": ">=7.2", + "composer/installers": "~1.0" } +} +``` Be sure to start your package `name` with **wn-** and end it with **-plugin** or **-theme** respectively - this will help others find your package and is in accordance with the [quality guidelines](../help/developer/guide#repository-naming). @@ -166,47 +168,51 @@ However, this can create problems with Winter's plugin oriented design, since th You may place this code in your Plugin registration file and call it from the the `boot()` method. - public function bootPackages() - { - // Get the namespace code of the current plugin - $pluginNamespace = str_replace('\\', '.', strtolower(__NAMESPACE__)); - - // Locate the packages to boot - $packages = \Config::get($pluginNamespace . '::packages'); - - // Boot each package - foreach ($packages as $name => $options) { - // Apply the configuration for the package - if ( - !empty($options['config']) && - !empty($options['config_namespace']) - ) { - Config::set($options['config_namespace'], $options['config']); - } +```php +public function bootPackages() +{ + // Get the namespace code of the current plugin + $pluginNamespace = str_replace('\\', '.', strtolower(__NAMESPACE__)); + + // Locate the packages to boot + $packages = \Config::get($pluginNamespace . '::packages'); + + // Boot each package + foreach ($packages as $name => $options) { + // Apply the configuration for the package + if ( + !empty($options['config']) && + !empty($options['config_namespace']) + ) { + Config::set($options['config_namespace'], $options['config']); } } +} +``` Now you are free to provide the packages configuration values the same way you would with regular plugin configuration values. - return [ - // Laravel Package Configuration - 'packages' => [ - 'packagevendor/packagename' => [ - // The accessor for the config item, for example, - // to access via Config::get('purifier.' . $key) - 'config_namespace' => 'purifier', - - // The configuration file for the package itself. - // Copy this from the package configuration. - 'config' => [ - 'encoding' => 'UTF-8', - 'finalize' => true, - 'cachePath' => storage_path('app/purifier'), - 'cacheFileMode' => 0755, - ], +```php +return [ + // Laravel Package Configuration + 'packages' => [ + 'packagevendor/packagename' => [ + // The accessor for the config item, for example, + // to access via Config::get('purifier.' . $key) + 'config_namespace' => 'purifier', + + // The configuration file for the package itself. + // Copy this from the package configuration. + 'config' => [ + 'encoding' => 'UTF-8', + 'finalize' => true, + 'cachePath' => storage_path('app/purifier'), + 'cacheFileMode' => 0755, ], ], - ]; + ], +]; +``` Now the package configuration has been included natively in Winter CMS and the values can be changed normally using the [standard configuration approach](../plugin/settings#file-configuration). diff --git a/markup-filter-app.md b/markup-filter-app.md index e8b6a695..3a15b4a5 100644 --- a/markup-filter-app.md +++ b/markup-filter-app.md @@ -2,22 +2,30 @@ The `| app` filter returns an address relative to the public path of the website. The result is an absolute URL, including the domain name and protocol, to the location specified in the filter parameter. The filter can be applied to any path. - +```twig + +``` If the website address is __https://example.com__ the above example would output the following: - +```twig + +``` It can also be used for static URLs: - - About Us - +```twig + + About Us + +``` The above would output: - - About us - +```html + + About us + +``` > **NOTE**: The `| page` filter is recommended for linking to other pages. diff --git a/markup-filter-default.md b/markup-filter-default.md index 3a0ee582..47acacdd 100644 --- a/markup-filter-default.md +++ b/markup-filter-default.md @@ -2,14 +2,18 @@ The `| default` filter returns the value passed as the first argument if the filtered value is undefined or empty, otherwise the filtered value is returned. - {{ variable | default('The variable is not defined') }} +```twig +{{ variable | default('The variable is not defined') }} - {{ variable.foo | default('The foo property on variable is not defined') }} +{{ variable.foo | default('The foo property on variable is not defined') }} - {{ variable['foo'] | default('The foo key in variable is not defined') }} +{{ variable['foo'] | default('The foo key in variable is not defined') }} - {{ '' | default('The variable is empty') }} +{{ '' | default('The variable is empty') }} +``` When using the `default` filter on an expression that uses variables in some method calls, be sure to use the `default` filter whenever a variable can be undefined: - {{ variable.method(foo | default('bar')) | default('bar') }} +```twig +{{ variable.method(foo | default('bar')) | default('bar') }} +``` diff --git a/markup-filter-image-height.md b/markup-filter-image-height.md index 2cb34018..085d1687 100644 --- a/markup-filter-image-height.md +++ b/markup-filter-image-height.md @@ -4,9 +4,11 @@ The `| imageHeight` filter attempts to identify the width in pixels of the provi > **NOTE:** This filter does not support thumbnail (already resized) versions of FileModels being passed as the image source. - {% set resizedImage = 'banner.jpg' | media | resize(1920, 1080) %} - +```twig +{% set resizedImage = 'banner.jpg' | media | resize(1920, 1080) %} + +``` See the [image resizing docs](../services/image-resizing#resize-sources) for more information on what image sources are supported. -> **NOTE:** The image resizing functionality requires a cache driver that persists cache data between requests in order to function, `array` is not a supported cache driver if you wish to use this functionality. \ No newline at end of file +> **NOTE:** The image resizing functionality requires a cache driver that persists cache data between requests in order to function, `array` is not a supported cache driver if you wish to use this functionality. diff --git a/markup-filter-image-width.md b/markup-filter-image-width.md index 63fe6b24..8fecd176 100644 --- a/markup-filter-image-width.md +++ b/markup-filter-image-width.md @@ -4,9 +4,11 @@ The `| imageWidth` filter attempts to identify the width in pixels of the provid > **NOTE:** This filter does not support thumbnail (already resized) versions of FileModels being passed as the image source. - {% set resizedImage = 'banner.jpg' | media | resize(1920, 1080) %} - +```twig +{% set resizedImage = 'banner.jpg' | media | resize(1920, 1080) %} + +``` See the [image resizing docs](../services/image-resizing#resize-sources) for more information on what image sources are supported. -> **NOTE:** The image resizing functionality requires a cache driver that persists cache data between requests in order to function, `array` is not a supported cache driver if you wish to use this functionality. \ No newline at end of file +> **NOTE:** The image resizing functionality requires a cache driver that persists cache data between requests in order to function, `array` is not a supported cache driver if you wish to use this functionality. diff --git a/markup-filter-md.md b/markup-filter-md.md index dda9c343..41bd4f3f 100644 --- a/markup-filter-md.md +++ b/markup-filter-md.md @@ -2,8 +2,12 @@ The `| md` filter converts the value from Markdown to HTML format. - {{ '**Text** is bold.' | md }} +```twig +{{ '**Text** is bold.' | md }} +``` The above will output the following: -

Text is bold.

+```html +

Text is bold.

+``` diff --git a/markup-filter-media.md b/markup-filter-media.md index 9f7fe733..b07afa7b 100644 --- a/markup-filter-media.md +++ b/markup-filter-media.md @@ -2,8 +2,12 @@ The `| media` filter returns an address relative to the public path of the [media manager library](../cms/mediamanager). The result is a URL to the media file specified in the filter parameter. - +```twig + +``` If the media manager address is `https://cdn.wintercms.com` the above example would output the following: - +```twig + +``` diff --git a/markup-filter-page.md b/markup-filter-page.md index 49ae7064..cabad7a7 100644 --- a/markup-filter-page.md +++ b/markup-filter-page.md @@ -2,90 +2,116 @@ The `| page` filter creates a link to a page using a page file name, without an extension, as a parameter. For example, if there is the about.htm page you can use the following code to generate a link to it: - About Us +```twig +About Us +``` Remember that if you refer a page from a subdirectory you should specify the subdirectory name: - About Us +```twig +About Us +``` > **NOTE**: The [Themes documentation](../cms/themes#subdirectories) has more details on subdirectory usage. To access the link to a certain page from the PHP section, you can use `$this->pageUrl('page-name-without-extension')`: - == - pageUrl('blog/overview'); - } - ?> - == - {{ newsPage }} +```php +== +pageUrl('blog/overview'); +} +?> +== +{{ newsPage }} +``` You can create a link to the current page by filtering an empty string: - Refresh page +```twig +Refresh page +``` To get the link to the current page in PHP, you can use `$this->pageUrl('')` with an empty string. - == - pageUrl(''); - } - ?> - == - {{ currentUrl }} +```php +== +pageUrl(''); +} +?> +== +{{ currentUrl }} +``` ## Reverse routing When linking to a page that has URL parameters defined, the ` | page` filter supports reverse routing by passing an array as the first argument. - url = "/blog/post/:post_id" - == - [...] +```ini +url = "/blog/post/:post_id" +== +[...] +``` Given the above content is found in a CMS page file **post.htm** you can link to this page using: - - Blog post #10 - +```twig + + Blog post #10 + +``` If the website address is __https://example.com__ the above example would output the following: - - Blog post #10 - +```html + + Blog post #10 + +``` ## Persistent URL parameters If a URL parameter is already presented in the environment, the ` | page` filter will use it automatically. - url = "/blog/post/:post_id" +```ini +url = "/blog/post/:post_id" - url = "/blog/post/edit/:post_id" +url = "/blog/post/edit/:post_id" +``` If there are two pages, **post.htm** and **post-edit.htm**, with the above URLs defined, you can link to either page without needing to define the `post_id` parameter. - - Edit this post - +```twig + + Edit this post + +``` When the above markup appears on the **post.htm** page, it will output the following: - - Edit this post - +```html + + Edit this post + +``` The `post_id` value of *10* is already known and has persisted across the environments. You can disable this functionality by passing the 2nd argument as `false`: - - Unknown blog post - +```twig + + Unknown blog post + +``` Or by defining a different value: - - Blog post #6 - +```twig + + Blog post #6 + +``` diff --git a/markup-filter-raw.md b/markup-filter-raw.md index dc871e9f..fd723404 100644 --- a/markup-filter-raw.md +++ b/markup-filter-raw.md @@ -2,18 +2,22 @@ Output variables in Winter are automatically escaped, the `| raw` filter marks the value as being "safe" and will not be escaped if `raw` is the last filter applied. - {# This variable won't be escaped #} - {{ variable | raw }} +```twig +{# This variable won't be escaped #} +{{ variable | raw }} +``` Be careful when using the `raw` filter inside expressions: - {% set hello = 'Hello' %} - {% set hola = 'Hola' %} +```twig +{% set hello = 'Hello' %} +{% set hola = 'Hola' %} - {{ false ? 'Hola' : hello | raw }} +{{ false ? 'Hola' : hello | raw }} - {# The above will not render the same as #} - {{ false ? hola : hello | raw }} +{# The above will not render the same as #} +{{ false ? hola : hello | raw }} - {# But renders the same as #} - {{ (false ? hola : hello) | raw }} +{# But renders the same as #} +{{ (false ? hola : hello) | raw }} +``` diff --git a/markup-filter-resize.md b/markup-filter-resize.md index 2427946a..c7918f03 100644 --- a/markup-filter-resize.md +++ b/markup-filter-resize.md @@ -2,7 +2,9 @@ The `| resize` filter attempts to resize the provided image source using the provided resizing configuration and outputs a URL to the resized image. - +```twig + +``` If the filter can successfully resize the provided image, then a URL to the Winter image resizer (`/resize/$id/$targetUrl`) will be rendered as the output of this filter until the image has been successfully resized. Once the image has been resized any future calls to this filter with the specific image and configuration combination will instead output a direct URL to the resized image. @@ -16,4 +18,4 @@ See the [image resizing docs](../services/image-resizing#resize-parameters) for - [List of locations images can be resized from](../services/image-resizing#resize-sources) -> **NOTE:** The image resizing functionality requires a cache driver that persists cache data between requests in order to function, `array` is not a supported cache driver if you wish to use this functionality. \ No newline at end of file +> **NOTE:** The image resizing functionality requires a cache driver that persists cache data between requests in order to function, `array` is not a supported cache driver if you wish to use this functionality. diff --git a/markup-filter-theme.md b/markup-filter-theme.md index f7384dad..96d24acf 100644 --- a/markup-filter-theme.md +++ b/markup-filter-theme.md @@ -8,7 +8,7 @@ The `| theme` filter returns an address relative to the active theme path of the If the website address is __https://example.com__ and the active theme is called `website` the above example would output the following: -```twig +```html ``` @@ -19,4 +19,4 @@ The filter can also be used to interact with the [Asset Compiler](../services/as 'assets/css/styles1.css', 'assets/css/styles2.css' ] | theme }}" rel="stylesheet"> -``` \ No newline at end of file +``` diff --git a/markup-function-dump.md b/markup-function-dump.md index 473da17d..f4554138 100644 --- a/markup-function-dump.md +++ b/markup-function-dump.md @@ -2,12 +2,18 @@ The `dump()` function dumps information about a template variable. This is useful when debugging a template that does not behave as expected. - {{ dump(user) }} +```twig +{{ dump(user) }} +``` You can inspect several variables by passing them as additional arguments: - {{ dump(user, categories) }} +```twig +{{ dump(user, categories) }} +``` If you don't pass any value, all variables from the current context are dumped: - {{ dump() }} +```twig +{{ dump() }} +``` diff --git a/markup-function-form.md b/markup-function-form.md index 18d29aec..6b60ad40 100644 --- a/markup-function-form.md +++ b/markup-function-form.md @@ -2,11 +2,15 @@ Functions prefixed with `form_` perform tasks that are useful when dealing with forms. The helper maps directly to the `Form` PHP class and its methods. For example: - {{ form_close() }} +```twig +{{ form_close() }} +``` is the PHP equivalent of the following: - +```php + +``` > **NOTE**: Methods in *camelCase* should be converted to *snake_case*. @@ -14,87 +18,113 @@ is the PHP equivalent of the following: Outputs a standard `
` opening tag along with the `_session_key` and `_token` hidden fields for CSRF protection. If you are using the [AJAX Framework](../ajax/introduction), it is recommended that you use [`form_ajax()`](#form_ajax) instead. - {{ form_open() }} +```twig +{{ form_open() }} +``` Attributes can be passed in the first argument. - {{ form_open({ class: 'form-horizontal' }) }} +```twig +{{ form_open({ class: 'form-horizontal' }) }} +``` The above example would output as the following: - +```twig + +``` There are some special options that can also be used alongside the attributes. - {{ form_open({ request: 'onUpdate' }) }} +```twig +{{ form_open({ request: 'onUpdate' }) }} +``` The function support the following options: Option | Description ------------- | ------------- -**method** | Request method. Corresponds to the **method** FORM tag attribute. Eg: POST, GET, PUT, DELETE -**request** | A handler name to execute on the server when the form is posted. See the [Handling forms](../cms/pages#handling-forms) article for details about the event handlers. -**url** | Specifies URL to post the form to. Corresponds to the **action** FORM tag attribute. -**files** | Determines whether the form will submit files. Accepted values: **true** and **false**. -**model** | A model object for the form model binding. +`method` | Request method. Corresponds to the `method` FORM tag attribute. Eg: `POST`, `GET`, `PUT`, `DELETE` +`request` | A handler name to execute on the server when the form is posted. See the [Handling forms](../cms/pages#handling-forms) article for details about the event handlers. +`url` | Specifies URL to post the form to. Corresponds to the `action` FORM tag attribute. +`files` | Determines whether the form will submit files. Accepted values: `true` and `false`. +`model` | A model object for the form model binding. ## form_ajax() Outputs an AJAX enabled FORM opening tag. The first parameter of the `form_ajax()` function is the AJAX handler name. The handler can be defined in the layout or page [PHP section](../cms/themes#php-section) code, it can also be defined in a component. You may find more information about AJAX in the [AJAX Framework](../ajax/introduction) article. - {{ form_ajax('onUpdate') }} +```twig +{{ form_ajax('onUpdate') }} +``` Attributes can be passed in the second argument. - {{ form_ajax('onSave', { class: 'form-horizontal'}) }} +```twig +{{ form_ajax('onSave', { class: 'form-horizontal'}) }} +``` The above example would output as the following: - +```twig + +``` There are some special options that can also be used alongside the attributes. - {{ form_ajax('onDelete', { data: { id: 2 }, confirm: 'Really delete this record?' }) }} +```twig +{{ form_ajax('onDelete', { data: { id: 2 }, confirm: 'Really delete this record?' }) }} - {{ form_ajax('onRefresh', { update: { statistics: '#statsPanel' } }) }} +{{ form_ajax('onRefresh', { update: { statistics: '#statsPanel' } }) }} +``` > **NOTE:** When attempting to reference a component's alias with `__SELF__` as an argument to `form_ajax()` you must first build the string you wish to use outside of the call itself. Example: - {% set targetPartial = "'" ~ __SELF__ ~ "::statistics': '#statsPanel'" %} - {{ form_ajax('onUpdate', { update: targetPartial }) }} +```twig +{% set targetPartial = "'" ~ __SELF__ ~ "::statistics': '#statsPanel'" %} +{{ form_ajax('onUpdate', { update: targetPartial }) }} +``` The function support the following options: Option | Description ------------- | ------------- -**success** | JavaScript string to execute on successful result. -**error** | JavaScript string to execute on failed result. -**confirm** | A confirmation message to display before sending the request. -**redirect** | On successful result, redirect to a URL. -**update** | An array of partials to update on success in the following format: { 'partial': '#element' }. -**data** | Extra data to include with the request in the following format: { 'myvar': 'myvalue' }. +`success` | JavaScript string to execute on successful result. +`error` | JavaScript string to execute on failed result. +`confirm` | A confirmation message to display before sending the request. +`redirect` | On successful result, redirect to a URL. +`update` | An array of partials to update on success in the following format: `{ 'partial': '#element' }`. +`data` | Extra data to include with the request in the following format: `{ 'myvar': 'myvalue' }`. ## form_close() Outputs a standard FORM closing tag. This tag is generally available to provide consistency in usage. - {{ form_close() }} +```twig +{{ form_close() }} +``` The above example would output as the following: -
+```html + +``` ## Passing attributes to the generated element You can pass additional attributes to the `Form::open()` method by passing an array of attribute names and values to be rendered on the final generated `
` element. - 'example', 'class' => 'something')) ?> - // .. - +```php + 'example', 'class' => 'something')) ?> + // .. + +``` The above example would output the following: - +```html + -
+ +``` diff --git a/markup-function-html.md b/markup-function-html.md index 1eaae667..552e8b0f 100644 --- a/markup-function-html.md +++ b/markup-function-html.md @@ -2,11 +2,15 @@ Functions prefixed with `html_` perform tasks that are useful when dealing with html markup. The helper maps directly to the `Html` PHP class and its methods. For example: - {{ html_strip() }} +```twig +{{ html_strip() }} +``` is the PHP equivalent of the following: - +```php + +``` > **NOTE**: Methods in *camelCase* should be converted to *snake_case*. @@ -14,33 +18,45 @@ is the PHP equivalent of the following: Removes HTML from a string. - {{ html_strip('Hello world') }} +```twig +{{ html_strip('Hello world') }} +``` ## html_limit() Limits HTML with specific length with a proper tag handling. - {{ html_limit('

Post content...

', 100) }} +```twig +{{ html_limit('

Post content...

', 100) }} +``` To add a suffix when limit is applied, pass it as the third argument. Defaults to `...`. - {{ html_limit('

Post content...

', 100, '... Read more!') }} +```twig +{{ html_limit('

Post content...

', 100, '... Read more!') }} +``` ## html_clean() Cleans HTML to prevent most XSS attacks. - {{ html_clean('') }} +```twig +{{ html_clean('') }} +``` ## html_email() Obfuscates an e-mail address to prevent spam-bots from sniffing it. - {{ html_email('a@b.c') }} +```twig +{{ html_email('a@b.c') }} +``` For example: - Email me +```twig +Email me - - Email me + +Email me +``` diff --git a/markup-function-str.md b/markup-function-str.md index 5e13333d..77645508 100644 --- a/markup-function-str.md +++ b/markup-function-str.md @@ -2,11 +2,15 @@ Functions prefixed with `str_` perform tasks that are useful when dealing with strings. The helper maps directly to the `Str` PHP class and its methods. For example: - {{ str_camel() }} +```twig +{{ str_camel() }} +``` is the PHP equivalent of the following: - +```twig + +``` > **NOTE**: Methods in *camelCase* should be converted to *snake_case*. @@ -14,51 +18,69 @@ is the PHP equivalent of the following: Limit the number of characters in a string. - {{ str_limit('The quick brown fox...', 100) }} +```twig +{{ str_limit('The quick brown fox...', 100) }} +``` To add a suffix when limit is applied, pass it as the third argument. Defaults to `...`. - {{ str_limit('The quick brown fox...', 100, '... Read more!') }} +```twig +{{ str_limit('The quick brown fox...', 100, '... Read more!') }} +``` ## str_words() Limit the number of words in a string. - {{ str_words('The quick brown fox...', 100) }} +```twig +{{ str_words('The quick brown fox...', 100) }} +``` To add a suffix when limit is applied, pass it as the third argument. Defaults to `...`. - {{ str_words('The quick brown fox...', 100, '... Read more!') }} +```twig +{{ str_words('The quick brown fox...', 100, '... Read more!') }} +``` ## str_camel() Convert a value to *camelCase*. - // Outputs: helloWorld - {{ str_camel('hello world') }} +```twig +// Outputs: helloWorld +{{ str_camel('hello world') }} +``` ## str_studly() Convert a value to *StudlyCase*. - // Outputs: HelloWorld - {{ str_studly('hello world') }} +```twig +// Outputs: HelloWorld +{{ str_studly('hello world') }} +``` ## str_snake() Convert a value to *snake_case*. - // Outputs: hello_world - {{ str_snake('hello world') }} +```twig +// Outputs: hello_world +{{ str_snake('hello world') }} +``` The second argument can supply a delimiter. - // Outputs: hello---world - {{ str_snake('hello world', '---') }} +```twig +// Outputs: hello---world +{{ str_snake('hello world', '---') }} +``` ## str_plural() Get the plural form of an English word. - // Outputs: chickens - {{ str_plural('chicken') }} +```twig +// Outputs: chickens +{{ str_plural('chicken') }} +``` diff --git a/markup-tag-component.md b/markup-tag-component.md index 4a3e1e3a..465005f9 100644 --- a/markup-tag-component.md +++ b/markup-tag-component.md @@ -2,18 +2,24 @@ The `{% component %}` tag will parse the default markup content for a [CMS component](../cms/components) and display it on the page. Not all components provide default markup, the documentation for the plugin will guide in the correct usage. - {% component "blogPosts" %} +```twig +{% component "blogPosts" %} +``` This will render the component partial with a fixed name of **default.htm** and is essentially an alias for the following: - {% partial "blogPosts::default" %} +```twig +{% partial "blogPosts::default" %} +``` ## Variables Some components support [passing variables](../cms/components#component-variables) at render time: - {% component "blogPosts" postsPerPage="5" %} +```twig +{% component "blogPosts" postsPerPage="5" %} +``` ## Customizing components diff --git a/markup-tag-content.md b/markup-tag-content.md index 9ad819c3..33a067ab 100644 --- a/markup-tag-content.md +++ b/markup-tag-content.md @@ -2,57 +2,77 @@ The `{% content %}` tag will display a [CMS content block](../cms/content) on the page. To display content block called **contacts.htm** you pass the file name after the `content` tag quoted as a string. - {% content "contacts.htm" %} +```twig +{% content "contacts.htm" %} +``` A content block inside a subdirectory can be rendered in the same way. - {% content "sidebar/content.htm" %} +```twig +{% content "sidebar/content.htm" %} +``` > **NOTE**: The [Themes documentation](../cms/themes#subdirectories) has more details on subdirectory usage. Content blocks can be rendered as plain text: - {% content "readme.txt" %} +```twig +{% content "readme.txt" %} +``` You can also use Markdown syntax: - {% content "changelog.md" %} +```twig +{% content "changelog.md" %} +``` Content blocks can also be used in combination with [layout placeholders](../cms/layouts#placeholders): - {% put sidebar %} - {% content 'sidebar-content.htm' %} - {% endput %} +```twig +{% put sidebar %} + {% content 'sidebar-content.htm' %} +{% endput %} +``` ## Variables You can pass variables to content blocks by specifying them after the file name: - {% content "welcome.htm" name=user.name %} +```twig +{% content "welcome.htm" name=user.name %} +``` You can also assign new variables for use in the content: - {% content "location.htm" city="Vancouver" country="Canada" %} +```twig +{% content "location.htm" city="Vancouver" country="Canada" %} +``` Inside the content, variables can be accessed using a basic syntax using singular *curly brackets*: -

Country: {country}, city: {city}.

+```twig +

Country: {country}, city: {city}.

+``` You can also pass a collection of variables as a simple array: - {% content "welcome.htm" likes=[ - {name:'Dogs'}, - {name:'Fishing'}, - {name:'Golf'} - ] %} +```twig +{% content "welcome.htm" likes=[ + {name:'Dogs'}, + {name:'Fishing'}, + {name:'Golf'} +] %} +``` The collection of variables is accessed by using an opening and closing set of brackets: -
    - {likes} -
  • {name}
  • - {/likes} -
+```twig +
    + {likes} +
  • {name}
  • + {/likes} +
+``` > **NOTE**: Twig syntax is not supported in Content blocks, consider using a [CMS partial](../cms/partials) instead. diff --git a/markup-tag-flash.md b/markup-tag-flash.md index 467f0c0d..3a6e5440 100644 --- a/markup-tag-flash.md +++ b/markup-tag-flash.md @@ -2,43 +2,51 @@ The `{% flash %}` and `{% endflash %}` tags will render any flash messages stored in the user session, set by the `Flash` PHP class. The `message` variable inside will contain the flash message text and the markup inside will repeat for multiple flash messages. -
    - {% flash %} -
  • {{ message }}
  • - {% endflash %} -
- -You can use the `type` variable that represents the flash message type — **success**, **error**, **info** or **warning**. - +```twig +
    {% flash %} -
    - {{ message }} -
    +
  • {{ message }}
  • {% endflash %} +
+``` -You can also specify the `type` to filter flash messages of a given type. The next example will show only **success** messages, if there is an **error** message it won't be displayed. +You can use the `type` variable that represents the flash message type — `success`, `error`, `info` or `warning`. - {% flash success %} -
{{ message }}
- {% endflash %} +```twig +{% flash %} +
+ {{ message }} +
+{% endflash %} +``` + +You can also specify the `type` to filter flash messages of a given type. The next example will show only `success` messages, if there is an `error` message it won't be displayed. + +```twig +{% flash success %} +
{{ message }}
+{% endflash %} +``` ## Setting flash messages Flash messages can be set by [Components](../cms/components) or inside the page or layout [PHP section](../cms/themes#php-section) with the `Flash` class. - - {% for user in users %} -
  • {{ user.username }}
  • - {% endfor %} - +```twig +
      + {% for user in users %} +
    • {{ user.username }}
    • + {% endfor %} +
    +``` You can also access both keys and values: -
      - {% for key, user in users %} -
    • {{ key }}: {{ user.username }}
    • - {% endfor %} -
    +```twig +
      + {% for key, user in users %} +
    • {{ key }}: {{ user.username }}
    • + {% endfor %} +
    +``` If the collection is empty, you can render a replacement block by using else: -
      - {% for user in users %} -
    • {{ user.username }}
    • - {% else %} -
    • There are no users found
    • - {% endfor %} -
    +```twig +
      + {% for user in users %} +
    • {{ user.username }}
    • + {% else %} +
    • There are no users found
    • + {% endfor %} +
    +``` ## Looping a collection If you do need to iterate over a collection of numbers, you can use the `..` operator: - {% for i in 0..10 %} - - {{ i }} - {% endfor %} +```twig +{% for i in 0..10 %} + - {{ i }} +{% endfor %} +``` The above snippet of code would print all numbers from 0 to 10. It can also be useful with letters: - {% for letter in 'a'..'z' %} - - {{ letter }} - {% endfor %} +```twig +{% for letter in 'a'..'z' %} + - {{ letter }} +{% endfor %} +``` The `..` operator can take any expression at both sides: - {% for letter in 'a'|upper..'z'|upper %} - - {{ letter }} - {% endfor %} +```twig +{% for letter in 'a'|upper..'z'|upper %} + - {{ letter }} +{% endfor %} +``` ## Adding a condition Unlike in PHP there is no function to `break` or `continue` in a loop, however you can still filter the collection. The following example skips all the `users` which are not active: -
      - {% for user in users if user.active %} -
    • {{ user.username }}
    • - {% endfor %} -
    +```twig +
      + {% for user in users if user.active %} +
    • {{ user.username }}
    • + {% endfor %} +
    +``` ## The loop variable @@ -73,6 +87,8 @@ Variable | Description `loop.length` | The number of items in the collection `loop.parent` | The parent context - {% for user in users %} - {{ loop.index }} - {{ user.username }} - {% endfor %} +```twig +{% for user in users %} + {{ loop.index }} - {{ user.username }} +{% endfor %} +``` diff --git a/markup-tag-if.md b/markup-tag-if.md index 43af747d..771ce001 100644 --- a/markup-tag-if.md +++ b/markup-tag-if.md @@ -2,37 +2,45 @@ The `{% if %}` and `{% endif %}` tags will represent an expression and is comparable with the if statements of PHP. In the simplest form you can use it to test if an expression evaluates to `true`: - {% if online == false %} -

    The website is in maintenance mode.

    - {% endif %} +```twig +{% if online == false %} +

    The website is in maintenance mode.

    +{% endif %} +``` You can also test if an array is not empty: - {% if users %} -
      - {% for user in users %} -
    • {{ user.username }}
    • - {% endfor %} -
    - {% endif %} +```twig +{% if users %} +
      + {% for user in users %} +
    • {{ user.username }}
    • + {% endfor %} +
    +{% endif %} +``` > **NOTE**: If you want to test if the variable is defined, use `{% if users is defined %}` instead. You can also use `not` to check for values that evaluate to `false`: - {% if not user.subscribed %} -

    You are not subscribed to our mailing list.

    - {% endif %} +```twig +{% if not user.subscribed %} +

    You are not subscribed to our mailing list.

    +{% endif %} +``` For multiple expressions `{% elseif %}` and `{% else %}` can be used: - {% if kenny.sick %} - Kenny is sick. - {% elseif kenny.dead %} - You killed Kenny! You bastard!!! - {% else %} - Kenny looks okay so far. - {% endif %} +```twig +{% if kenny.sick %} + Kenny is sick. +{% elseif kenny.dead %} + You killed Kenny! You bastard!!! +{% else %} + Kenny looks okay so far. +{% endif %} +``` ## Expression rules @@ -40,10 +48,10 @@ The rules to determine if an expression is true or false are the same as in PHP, Value | Boolean evaluation ------------- | ------------- -*empty string* | false -*numeric zero* | false -*whitespace-only string* | true -*empty array* | false -*null* | false -*non-empty array* | true -*object* | true +*empty string* | `false` +*numeric zero* | `false` +*whitespace-only string* | `true` +*empty array* | `false` +*null* | `false` +*non-empty array* | `true` +*object* | `true` diff --git a/markup-tag-macro.md b/markup-tag-macro.md index adfcf6c4..5c7b5d7a 100644 --- a/markup-tag-macro.md +++ b/markup-tag-macro.md @@ -2,25 +2,31 @@ The `{% macro %}` tag allows you to define custom functions in your templates, similar to regular programming languages. - {% macro input() %} - ... - {% endmacro %} +```twig +{% macro input() %} + ... +{% endmacro %} +``` Alternatively you can include the name of the macro after the end tag for better readability: - {% macro input() %} - ... - {% endmacro input %} +```twig +{% macro input() %} + ... +{% endmacro input %} +``` The following example defines a function called `input()` that takes 4 arguments, the associated values are accessed as variables within the markup inside. - {% macro input(name, value, type, size) %} - - {% endmacro %} +```twig +{% macro input(name, value, type, size) %} + +{% endmacro %} +``` > **NOTE**: Macro arguments don't specify default values and are always considered optional. @@ -29,62 +35,76 @@ The following example defines a function called `input()` that takes 4 arguments Before a macro can be used it needs to be "imported" first using the `{% import %}` tag. If the macro is defined in the same template, the special `_self` variable can be used. - {% import _self as form %} +```twig +{% import _self as form %} +``` Here the macro functions are assigned to the `form` variable, available to be called like any other function. -

    {{ form.input('username') }}

    -

    {{ form.input('password', null, 'password') }}

    +```twig +

    {{ form.input('username') }}

    +

    {{ form.input('password', null, 'password') }}

    +``` Macros can be defined in [a theme partial](../cms/partials) and imported by name. To import the macros from a partial called **macros/form.htm**, simply pass the name after the `import` tag quoted as a string. - {% import 'macros/form' as form %} +```twig +{% import 'macros/form' as form %} +``` Alternatively you may import macros from a [system view file](../services/response-view#views) and these will be accepted. To import from **plugins/acme/blog/views/macros.htm** simply pass the path hint instead. - {% import 'acme.blog::macros' as form %} +```twig +{% import 'acme.blog::macros' as form %} +``` ## Nested macros When you want to use a macro inside another macro from the same template, you need to import it locally. - {% macro input(name, value, type, size) %} - - {% endmacro %} +```twig +{% macro input(name, value, type, size) %} + +{% endmacro %} - {% macro wrapped_input(name, value, type, size) %} - {% import _self as form %} +{% macro wrapped_input(name, value, type, size) %} + {% import _self as form %} -
    - {{ form.input(name, value, type, size) }} -
    - {% endmacro %} +
    + {{ form.input(name, value, type, size) }} +
    +{% endmacro %} +``` ## Context variable Macros don't have access to the current page variables. - - {{ site_name }} +```twig + +{{ site_name }} - {% macro myFunction() %} - - {{ site_name }} - {% endmacro %} +{% macro myFunction() %} + + {{ site_name }} +{% endmacro %} +``` You may pass the variables to the function using the special `_context` variable. - {% macro myFunction(vars) %} - {{ vars.site_name }} - {% endmacro %} +```twig +{% macro myFunction(vars) %} + {{ vars.site_name }} +{% endmacro %} - {% import _self as form %} +{% import _self as form %} - - {{ form.myFunction(_context) }} + +{{ form.myFunction(_context) }} +``` diff --git a/markup-tag-page.md b/markup-tag-page.md index 77314108..4485f0d9 100644 --- a/markup-tag-page.md +++ b/markup-tag-page.md @@ -6,31 +6,36 @@ See [layouts](../cms/layouts#introduction) for a basic example. The `{% page %}` tag parses the raw markup from a page template. A page template may inject content both into placeholder(s) as well as define raw markup. - description="example layout" - == - - - {% placeholder head %} - - - {% page %} - ... - - description="example page" - == - {% put head %} - - {% endput %} - -

    My content.

    - +```twig +description="example layout" +== + + + {% placeholder head %} + + + {% page %} + ... +``` + +```twig +description="example page" +== +{% put head %} + +{% endput %} + +

    My content.

    +``` + The page rendered with the template would result in: - - - - - -

    My content.

    - ... - \ No newline at end of file +```twig + + + + + +

    My content.

    + ... +``` diff --git a/markup-tag-partial.md b/markup-tag-partial.md index bd97ca9a..552985af 100644 --- a/markup-tag-partial.md +++ b/markup-tag-partial.md @@ -2,43 +2,57 @@ The `{% partial %}` tag will parse a [CMS partial](../cms/partials) and render the partial contents on the page. To display a partial called **footer.htm**, simply pass the name after the `partial` tag quoted as a string. - {% partial "footer" %} +```twig +{% partial "footer" %} +``` A partial inside a subdirectory can be rendered in the same way. - {% partial "sidebar/menu" %} +```twig +{% partial "sidebar/menu" %} +``` > **NOTE**: The [Themes documentation](../cms/themes#subdirectories) has more details on subdirectory usage. The partial name can also be a variable: - {% set tabName = "profile" %} - {% partial tabName %} +```twig +{% set tabName = "profile" %} +{% partial tabName %} +``` ## Variables You can pass variables to partials by specifying them after the partial name: - {% partial "blog-posts" posts=posts %} +```twig +{% partial "blog-posts" posts=posts %} +``` You can also assign new variables for use in the partial: - {% partial "location" city="Vancouver" country="Canada" %} +```twig +{% partial "location" city="Vancouver" country="Canada" %} +``` Inside the partial, variables can be accessed like any other markup variable: -

    Country: {{ country }}, city: {{ city }}.

    +```twig +

    Country: {{ country }}, city: {{ city }}.

    +``` ## Checking a partial exists In any template you can check if a partial content exists by using the `partial()` function. This lets you to generate different markup depending on whether the partial exists or not. Example: - {% set cardPartial = 'my-cards/' ~ cardCode %} +```twig +{% set cardPartial = 'my-cards/' ~ cardCode %} - {% if partial(cardPartial) %} - {% partial cardPartial %} - {% else %} -

    Card not found!

    - {% endif %} +{% if partial(cardPartial) %} + {% partial cardPartial %} +{% else %} +

    Card not found!

    +{% endif %} +``` diff --git a/markup-tag-placeholder.md b/markup-tag-placeholder.md index effb7a09..7d2fa21d 100644 --- a/markup-tag-placeholder.md +++ b/markup-tag-placeholder.md @@ -2,59 +2,73 @@ The `{% placeholder %}` tag will render a placeholder section which is generally [used inside Layouts](../cms/layouts#placeholders). This tag will return any placeholder contents that have been added using the `{% put %}` tag, or any default content that is defined (optional). - {% placeholder name %} +```twig +{% placeholder name %} +``` Content can then be injected into the placeholder in any subsequent page or partial. - {% put name %} -

    Place this text in the name placeholder

    - {% endput %} +```twig +{% put name %} +

    Place this text in the name placeholder

    +{% endput %} +``` ## Default placeholder content Placeholders can have default content that can be either replaced or complemented by a page. If the `{% put %}` tag for a placeholder with default content is not defined on a page, the default placeholder content is displayed. Example placeholder definition in the layout template: - {% placeholder sidebar default %} -

    Contact us

    - {% endplaceholder %} +```twig +{% placeholder sidebar default %} +

    Contact us

    +{% endplaceholder %} +``` The page can inject more content to the placeholder. The `{% default %}` tag specifies a place where the default placeholder content should be displayed. If the tag is not used the placeholder content is completely replaced. - {% put sidebar %} -

    Services

    - {% default %} - {% endput %} +```twig +{% put sidebar %} +

    Services

    + {% default %} +{% endput %} +``` ## Checking a placeholder exists In a layout template you can check if a placeholder content exists by using the `placeholder()` function. This lets you to generate different markup depending on whether the page provides a placeholder content. Example: - {% if placeholder('sidemenu') %} - -
    -
    - {% placeholder sidemenu %} -
    -
    - {% page %} -
    +```twig +{% if placeholder('sidemenu') %} + +
    +
    + {% placeholder sidemenu %}
    - {% else %} - - {% page %} - {% endif %} +
    + {% page %} +
    +
    +{% else %} + + {% page %} +{% endif %} +``` ## Custom attributes The `placeholder` tag accepts two optional attributes — `title` and `type`. The `title` attribute is not used by the CMS itself, but could be used by other plugins. The type attribute manages the placeholder type. There are two types supported at the moment — **text** and **html**. The content of text placeholders is escaped before it's displayed. The title and type attributes should be defined after the placeholder name and the `default` attribute, if it's presented. Example: - {% placeholder ordering title="Ordering information" type="text" %} +```twig +{% placeholder ordering title="Ordering information" type="text" %} +``` Example of a placeholder with a default content, title and type attributes. - {% placeholder ordering default title="Ordering information" type="text" %} - There is no ordering information for this product. - {% endplaceholder %} +```twig +{% placeholder ordering default title="Ordering information" type="text" %} + There is no ordering information for this product. +{% endplaceholder %} +``` diff --git a/markup-tag-verbatim.md b/markup-tag-verbatim.md index 39969dda..a7dd7e09 100644 --- a/markup-tag-verbatim.md +++ b/markup-tag-verbatim.md @@ -2,16 +2,22 @@ The `{% verbatim %}` tag marks entire sections as being raw text that should not be parsed. - {% verbatim %}

    Hello, {{ name }}

    {% endverbatim %} +```twig +{% verbatim %}

    Hello, {{ name }}

    {% endverbatim %} +``` The above will render in the browser exactly as: -

    Hello, {{ name }}

    +```twig +

    Hello, {{ name }}

    +``` For example, AngularJS uses the same templating syntax so you can decide which variables to use for each. -

    Hello {{ name }}, this is parsed by Twig

    +```twig +

    Hello {{ name }}, this is parsed by Twig

    - {% verbatim %} -

    Hello {{ name }}, this is parsed by AngularJS

    - {% endverbatim %} +{% verbatim %} +

    Hello {{ name }}, this is parsed by AngularJS

    +{% endverbatim %} +``` diff --git a/markup-templating.md b/markup-templating.md index a0047c18..4fc69d1a 100644 --- a/markup-templating.md +++ b/markup-templating.md @@ -6,15 +6,21 @@ Winter extends the [Twig template language](https://twig.symfony.com/doc/2.x/) w Template variables are printed on the page using *double curly brackets*. - {{ variable }} +```twig +{{ variable }} +``` Variables can also represent *expressions*. - {{ isAjax ? 'Yes' : 'No' }} +```twig +{{ isAjax ? 'Yes' : 'No' }} +``` Variables can be concatenated with the `~` character. - {{ 'Your name: ' ~ name }} +```twig +{{ 'Your name: ' ~ name }} +``` Winter provides global variables under the `this` variable, as listed under the **Variables** section. @@ -22,19 +28,25 @@ Winter provides global variables under the `this` variable, as listed under the Tags are a unique feature to Twig and are wrapped with `{% %}` characters. - {% tag %} +```twig +{% tag %} +``` Tags provide a more fluent way to describe template logic. - {% if stormCloudComing %} - Stay inside - {% else %} - Go outside and play - {% endif %} +```twig +{% if stormCloudComing %} + Stay inside +{% else %} + Go outside and play +{% endif %} +``` The `{% set %}` tag can be used to set variables inside the template. - {% set activePage = 'blog' %} +```twig +{% set activePage = 'blog' %} +``` Tags can take on many different syntaxes and are listed under the **Tags** section. @@ -42,15 +54,21 @@ Tags can take on many different syntaxes and are listed under the **Tags** secti Filters act as modifiers to variables for a single instance and are applied using a *pipe symbol* followed by the filter name. - {{ 'string'|filter }} +```twig +{{ 'string'|filter }} +``` Filters can take arguments like a function. - {{ price|currency('USD') }} +```twig +{{ price|currency('USD') }} +``` Filters can be applied in succession. - {{ 'Winter Glory'|upper|replace({'Winter': 'Morning'}) }} +```twig +{{ 'Winter Glory'|upper|replace({'Winter': 'Morning'}) }} +``` Filters are listed under the **Filters** section. @@ -58,11 +76,15 @@ Filters are listed under the **Filters** section. Functions allow logic to be executed and the return result acts as a variable. - {{ function() }} +```twig +{{ function() }} +``` Functions can take arguments. - {{ dump(variable) }} +```twig +{{ dump(variable) }} +``` Functions are listed under the **Functions** section. diff --git a/markup-this-environment.md b/markup-this-environment.md index dfc98c62..b2f33d5e 100644 --- a/markup-this-environment.md +++ b/markup-this-environment.md @@ -6,8 +6,10 @@ You can access the current environment object via `this.environment` and it retu The following example will display a banner if the website is running in the test environment: - {% if this.environment == 'test' %} +```twig +{% if this.environment == 'test' %} - + - {% endif %} +{% endif %} +``` diff --git a/markup-this-layout.md b/markup-this-layout.md index b4b44ecb..bf24d2a8 100644 --- a/markup-this-layout.md +++ b/markup-this-layout.md @@ -10,7 +10,9 @@ You can access the current layout object via `this.layout` and it returns the ob Converts the layout file name and folder name to a CSS friendly identifier. - +```twig + +``` If the layout file was **default.htm** this would generate a class name of `layout-default`. @@ -18,4 +20,6 @@ If the layout file was **default.htm** this would generate a class name of `layo The layout description as defined by the configuration. - +```twig + +``` diff --git a/markup-this-page.md b/markup-this-page.md index 4fdbbfda..41ddc2d4 100644 --- a/markup-this-page.md +++ b/markup-this-page.md @@ -10,13 +10,17 @@ You can access the current page object via `this.page` and it returns the object Reference to the layout name used by this page, if defined. Not to be confused with `this.layout`. - {{ this.page.layout }} +```twig +{{ this.page.layout }} +``` ### id Converts the page file name and folder name to a CSS friendly identifier. - +```twig + +``` If the page file was **home/index.htm** this would generate a class name of `page-home-index`. @@ -24,33 +28,43 @@ If the page file was **home/index.htm** this would generate a class name of `pag The page title as defined by the configuration. -

    {{ this.page.title }}

    +```twig +

    {{ this.page.title }}

    +``` ### description The page description as defined by the configuration. -

    {{ this.page.description }}

    +```twig +

    {{ this.page.description }}

    +``` ### meta_title An alternative `title` field, usually more descriptive for SEO purposes. - {{ this.page.meta_title }} +```twig +{{ this.page.meta_title }} +``` ### meta_description An alternative `description` field, usually more descriptive for SEO purposes. - +```twig + +``` ### hidden Hidden pages are accessible only by logged-in backend users. - {% if this.page.hidden %} -

    Note to other admins: We are currently working on this page.

    - {% endif %} +```twig +{% if this.page.hidden %} +

    Note to other admins: We are currently working on this page.

    +{% endif %} +``` ### fileName diff --git a/markup-this-param.md b/markup-this-param.md index 925d37cc..e21d0c86 100644 --- a/markup-this-param.md +++ b/markup-this-param.md @@ -6,22 +6,26 @@ You can access the current URL parameters via `this.param` and it returns a PHP This example demonstrates how to access the `tab` URL parameter in a page. - url = "/account/:tab" - == - {% if this.param.tab == 'details' %} +```twig +url = "/account/:tab" +== +{% if this.param.tab == 'details' %} -

    Here are all your details

    +

    Here are all your details

    - {% elseif this.param.tab == 'history' %} +{% elseif this.param.tab == 'history' %} -

    You are viewing a blast from the past

    +

    You are viewing a blast from the past

    - {% endif %} +{% endif %} +``` If the parameter name is also a variable, then array syntax can be used. - url = "/account/:post_id" - == - {% set name = 'post_id' %} +```twig +url = "/account/:post_id" +== +{% set name = 'post_id' %} -

    The post ID is: {{ this.param[name] }}

    +

    The post ID is: {{ this.param[name] }}

    +``` diff --git a/markup-this-session.md b/markup-this-session.md index 3f5e0a0a..490368f7 100644 --- a/markup-this-session.md +++ b/markup-this-session.md @@ -4,26 +4,34 @@ You can access the current session manager via `this.session` and it returns the ## Storing data in the session - {{ this.session.put('key', 'value') }} +```twig +{{ this.session.put('key', 'value') }} +``` ## Retrieving data from the session - {{ this.session.get('key') }} +```twig +{{ this.session.get('key') }} +``` ## Determining if an item exists in the session - {% if this.session.has('key') %} -

    we found it in the session

    - {% endif %} +```twig +{% if this.session.has('key') %} +

    we found it in the session

    +{% endif %} +``` ## Deleting data from the session #### Remove data for a single key - {{ this.session.forget('key') }} +```twig +{{ this.session.forget('key') }} +``` #### Remove all session data - {{ this.session.flush() }} - - +```twig +{{ this.session.flush() }} +``` diff --git a/markup-this-theme.md b/markup-this-theme.md index 0f31beba..0b75ad5b 100644 --- a/markup-this-theme.md +++ b/markup-this-theme.md @@ -10,7 +10,9 @@ You can access the current theme object via `this.theme` and it returns the obje Converts the theme directory name to a CSS friendly identifier. - +```twig + +``` If the theme directory was **website** this would generate a class name of `theme-website`. @@ -18,4 +20,6 @@ If the theme directory was **website** this would generate a class name of `them An array containing all the theme configuration values found in the `theme.yaml` file. - +```twig + +``` diff --git a/plugin-components.md b/plugin-components.md index 21cd59d5..3f8e516d 100644 --- a/plugin-components.md +++ b/plugin-components.md @@ -42,51 +42,59 @@ Components must be [registered in the Plugin registration class](#component-regi The **component class file** defines the component functionality and [component properties](#component-properties). The component class file name should match the component class name. Component classes should extend the `\Cms\Classes\ComponentBase` class. The component from the next example should be defined in the plugins/acme/blog/components/BlogPosts.php file. - namespace Acme\Blog\Components; +```php +namespace Acme\Blog\Components; + +class BlogPosts extends \Cms\Classes\ComponentBase +{ + public function componentDetails() + { + return [ + 'name' => 'Blog Posts', + 'description' => 'Displays a collection of blog posts.' + ]; + } - class BlogPosts extends \Cms\Classes\ComponentBase + // This array becomes available on the page as {{ component.posts }} + public function posts() { - public function componentDetails() - { - return [ - 'name' => 'Blog Posts', - 'description' => 'Displays a collection of blog posts.' - ]; - } - - // This array becomes available on the page as {{ component.posts }} - public function posts() - { - return ['First Post', 'Second Post', 'Third Post']; - } + return ['First Post', 'Second Post', 'Third Post']; } +} +``` The `componentDetails` method is required. The method should return an array with two keys: `name` and `description`. The name and description are display in the CMS backend user interface. When this [component is attached to a page or layout](../cms/components), the class properties and methods become available on the page through the component variable, which name matches the component short name or the alias. For example, if the BlogPost component from the previous example was defined on a page with its short name: - url = "/blog" +```ini +url = "/blog" - [blogPosts] - == +[blogPosts] +== +``` You would be able to access its `posts` method through the `blogPosts` variable. Note that Twig supports the property notation for methods, so that you don't need to use brackets. - {% for post in blogPosts.posts %} - {{ post }} - {% endfor %} +```twig +{% for post in blogPosts.posts %} + {{ post }} +{% endfor %} +``` ### Component registration Components must be registered by overriding the `registerComponents` method inside the [Plugin registration class](registration#registration-file). This tells the CMS about the Component and provides a **short name** for using it. An example of registering a component: - public function registerComponents() - { - return [ - 'Winter\Demo\Components\Todo' => 'demoTodo' - ]; - } +```php +public function registerComponents() +{ + return [ + 'Winter\Demo\Components\Todo' => 'demoTodo' + ]; +} +``` This will register the Todo component class with the default alias name **demoTodo**. More information on using components can be found at the [CMS components article](../cms/components). @@ -95,22 +103,30 @@ This will register the Todo component class with the default alias name **demoTo When you add a component to a page or layout you can configure it using properties. The properties are defined with the `defineProperties` method of the component class. The next example shows how to define a component property: - public function defineProperties() - { - return [ - 'maxItems' => [ - 'title' => 'Max items', - 'description' => 'The most amount of todo items allowed', - 'default' => 10, - 'type' => 'string', - 'validationPattern' => '^[0-9]+$', - 'validationMessage' => 'The Max Items property can contain only numeric symbols' - ] - ]; - } +```php +public function defineProperties() +{ + return [ + 'maxItems' => [ + 'title' => 'Max items', + 'description' => 'The most amount of todo items allowed', + 'default' => 10, + 'type' => 'string', + 'validationPattern' => '^[0-9]+$', + 'validationMessage' => 'The Max Items property can contain only numeric symbols' + ] + ]; +} +``` The method should return an array with the property keys as indexes and property parameters as values. The property keys are used for accessing the component property values inside the component class. The property parameters are defined with an array with the following keys: + +
    + Key | Description ------------- | ------------- `title` | required, the property title, it is used by the component Inspector in the CMS backend. @@ -128,155 +144,185 @@ Key | Description Inside the component you can read the property value with the `property` method: - $this->property('maxItems'); +```php +$this->property('maxItems'); +``` If the property value is not defined, you can supply the default value as a second parameter of the `property` method: - $this->property('maxItems', 6); +```php +$this->property('maxItems', 6); +``` You can also load all the properties as array: - $properties = $this->getProperties(); +```php +$properties = $this->getProperties(); +``` To access the property from the Twig partials for the component, utilize the `__SELF__` variable which refers to the Component object: - `{{ __SELF__.property('maxItems') }}` +```twig +{{ __SELF__.property('maxItems') }} +``` ### Dropdown and Set properties The option list for dropdown and set properties can be static or dynamic. Static options are defined with the `options` element of the property definition. Example: - public function defineProperties() - { - return [ - 'units' => [ - 'title' => 'Units', - 'type' => 'dropdown', - 'default' => 'imperial', - 'placeholder' => 'Select units', - 'options' => ['metric'=>'Metric', 'imperial'=>'Imperial'] - ] - ]; - } +```php +public function defineProperties() +{ + return [ + 'units' => [ + 'title' => 'Units', + 'type' => 'dropdown', + 'default' => 'imperial', + 'placeholder' => 'Select units', + 'options' => ['metric'=>'Metric', 'imperial'=>'Imperial'] + ] + ]; +} +``` The list of options could be fetched dynamically from the server when the Inspector is displayed. If the `options` parameter is omitted in a dropdown or set property definition the option list is considered dynamic. The component class must define a method returning the option list. The method should have a name in the following format: `get*Property*Options`, where **Property** is the property name, for example: `getCountryOptions`. The method returns an array of options with the option values as keys and option labels as values. Example of a dynamic dropdown list definition: - public function defineProperties() - { - return [ - 'country' => [ - 'title' => 'Country', - 'type' => 'dropdown', - 'default' => 'us' - ] - ]; - } +```php +public function defineProperties() +{ + return [ + 'country' => [ + 'title' => 'Country', + 'type' => 'dropdown', + 'default' => 'us' + ] + ]; +} - public function getCountryOptions() - { - return ['us'=>'United states', 'ca'=>'Canada']; - } +public function getCountryOptions() +{ + return ['us'=>'United states', 'ca'=>'Canada']; +} +``` Dynamic dropdown and set lists can depend on other properties. For example, the state list could depend on the selected country. The dependencies are declared with the `depends` parameter in the property definition. The next example defines two dynamic dropdown properties and the state list depends on the country: - public function defineProperties() - { - return [ - 'country' => [ - 'title' => 'Country', - 'type' => 'dropdown', - 'default' => 'us' - ], - 'state' => [ - 'title' => 'State', - 'type' => 'dropdown', - 'default' => 'dc', - 'depends' => ['country'], - 'placeholder' => 'Select a state' - ] - ]; - } +```php +public function defineProperties() +{ + return [ + 'country' => [ + 'title' => 'Country', + 'type' => 'dropdown', + 'default' => 'us' + ], + 'state' => [ + 'title' => 'State', + 'type' => 'dropdown', + 'default' => 'dc', + 'depends' => ['country'], + 'placeholder' => 'Select a state' + ] + ]; +} +``` In order to load the state list you should know what country is currently selected in the Inspector. The Inspector POSTs all property values to the `getPropertyOptions` handler, so you can do the following: - public function getStateOptions() - { - $countryCode = Request::input('country'); // Load the country property value from POST +```php +public function getStateOptions() +{ + $countryCode = Request::input('country'); // Load the country property value from POST - $states = [ - 'ca' => ['ab'=>'Alberta', 'bc'=>'British columbia'], - 'us' => ['al'=>'Alabama', 'ak'=>'Alaska'] - ]; + $states = [ + 'ca' => ['ab'=>'Alberta', 'bc'=>'British columbia'], + 'us' => ['al'=>'Alabama', 'ak'=>'Alaska'] + ]; - return $states[$countryCode]; - } + return $states[$countryCode]; +} +``` ### Page list properties Sometimes components need to create links to the website pages. For example, the blog post list contains links to the blog post details page. In this case the component should know the post details page file name (then it can use the [page Twig filter](../markup/filter-page)). Winter includes a helper for creating dynamic dropdown page lists. The next example defines the postPage property which displays a list of pages: - public function defineProperties() - { - return [ - 'postPage' => [ - 'title' => 'Post page', - 'type' => 'dropdown', - 'default' => 'blog/post' - ] - ]; - } +```php +public function defineProperties() +{ + return [ + 'postPage' => [ + 'title' => 'Post page', + 'type' => 'dropdown', + 'default' => 'blog/post' + ] + ]; +} - public function getPostPageOptions() - { - return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName'); - } +public function getPostPageOptions() +{ + return Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName'); +} +``` ## Routing parameters Components can directly access routing parameter values defined in the [URL of the page](../cms/pages#url-syntax). - // Returns the URL segment value, eg: /page/:post_id - $postId = $this->param('post_id'); +```php +// Returns the URL segment value, eg: /page/:post_id +$postId = $this->param('post_id'); +``` In some cases a [component property](#component-properties) may act as a hard coded value or reference the value from the URL. This hard coded example shows the blog post with an identifier `2` being used: - url = "/blog/hard-coded-page" +```ini +url = "/blog/hard-coded-page" - [blogPost] - id = "2" +[blogPost] +id = "2" +``` Alternatively the value can be referenced dynamically from the page URL using an [external property value](../cms/components#external-property-values): - url = "/blog/:my_custom_parameter" +```ini +url = "/blog/:my_custom_parameter" - [blogPost] - id = "{{ :my_custom_parameter }}" +[blogPost] +id = "{{ :my_custom_parameter }}" +``` In both cases the value can be retrieved by using the `property` method: - $this->property('id'); +```php +$this->property('id'); +``` If you need to access the routing parameter name: - $this->paramName('id'); // Returns "my_custom_parameter" +```php +$this->paramName('id'); // Returns "my_custom_parameter" +``` ## Handling the page execution cycle Components can be involved in the Page execution cycle events by overriding the `onRun` method in the component class. The CMS controller executes this method every time when the page or layout loads. Inside the method you can inject variables to the Twig environment through the `page` property: - public function onRun() - { - // This code will be executed when the page or layout is - // loaded and the component is attached to it. +```php +public function onRun() +{ + // This code will be executed when the page or layout is + // loaded and the component is attached to it. - $this->page['var'] = 'value'; // Inject some variable to the page - } + $this->page['var'] = 'value'; // Inject some variable to the page +} +``` ### Page execution life cycle handlers @@ -298,44 +344,52 @@ When a page loads, Winter executes handler functions that could be defined in th Sometimes you may wish to execute code at the time the component class is first instantiated. You may override the `init` method in the component class to handle any initialization logic, this will execute before AJAX handlers and before the page execution life cycle. For example, this method can be used for attaching another component to the page dynamically. - public function init() - { - $this->addComponent('Acme\Blog\Components\BlogPosts', 'blogPosts'); - } +```php +public function init() +{ + $this->addComponent('Acme\Blog\Components\BlogPosts', 'blogPosts'); +} +``` ### Halting with a response Like all methods in the [page execution life cycle](../cms/layouts#layout-life-cycle), if the `onRun` method in a component returns a value, this will stop the cycle at this point and return the response to the browser. Here we return an access denied message using the `Response` facade: - public function onRun() - { - if (true) { - return Response::make('Access denied!', 403); - } +```php +public function onRun() +{ + if (true) { + return Response::make('Access denied!', 403); } +} +``` You can also return a 404 response from the `onRun` method: - public function onRun() - { - if (true) { - $this->setStatusCode(404); - return $this->controller->run('404'); - } +```php +public function onRun() +{ + if (true) { + $this->setStatusCode(404); + return $this->controller->run('404'); } +} +``` ## AJAX handlers Components can host AJAX event handlers. They are defined in the component class exactly like they can be defined in the [page or layout code](../ajax/handlers). An example AJAX handler method defined in a component class: - public function onAddItem() - { - $value1 = post('value1'); - $value2 = post('value2'); - $this->page['result'] = $value1 + $value2; - } +```php +public function onAddItem() +{ + $value1 = post('value1'); + $value2 = post('value2'); + $this->page['result'] = $value1 + $value2; +} +``` If the alias for this component was *demoTodo* this handler can be accessed by `demoTodo::onAddItem`. Please see the [Calling AJAX handlers defined in components](../ajax/handlers#calling-handlers) article for details about using AJAX with components. @@ -346,100 +400,128 @@ All components can come with default markup that is used when including it on a The default component markup should be placed in a file named **default.htm**. For example, the default markup for the Demo ToDo component is defined in the file **/plugins/winter/demo/components/todo/default.htm**. It can then be inserted anywhere on the page by using the `{% component %}` tag: - url = "/todo" +```twig +url = "/todo" - [demoTodo] - == - {% component 'demoTodo' %} +[demoTodo] +== +{% component 'demoTodo' %} +``` The default markup can also take parameters that override the [component properties](#component-properties) at the time they are rendered. - {% component 'demoTodo' maxItems="7" %} +```twig +{% component 'demoTodo' maxItems="7" %} +``` These properties will not be available in the `onRun` method since they are established after the page cycle has completed. Instead they can be processed by overriding the `onRender` method in the component class. The CMS controller executes this method before the default markup is rendered. - public function onRender() - { - // This code will be executed before the default component - // markup is rendered on the page or layout. +```php +public function onRender() +{ + // This code will be executed before the default component + // markup is rendered on the page or layout. - $this->page['var'] = 'Maximum items allowed: ' . $this->property('maxItems'); - } + $this->page['var'] = 'Maximum items allowed: ' . $this->property('maxItems'); +} +``` ## Component partials In addition to the default markup, components can also offer additional partials that can be used on the frontend or within the default markup itself. If the Demo ToDo component had a **pagination** partial, it would be located in **/plugins/winter/demo/components/todo/pagination.htm** and displayed on the page using: - {% partial 'demoTodo::pagination' %} +```twig +{% partial 'demoTodo::pagination' %} +``` A relaxed method can be used that is contextual. If called inside a component partial, it will directly refer to itself. If called inside a theme partial, it will scan all components used on the page/layout for a matching partial name and use that. - {% partial '@pagination' %} +```twig +{% partial '@pagination' %} +``` Multiple components can share partials by placing the partial file in a directory called **components/partials**. The partials found in this directory are used as a fallback when the usual component partial cannot be found. For example, a shared partial located in **/plugins/acme/blog/components/partials/shared.htm** can be displayed on the page by any component using: - {% partial '@shared' %} +```twig +{% partial '@shared' %} +``` ### Referencing "self" Components can reference themselves inside their partials by using the `__SELF__` variable. By default it will return the component's short name or [alias](../cms/components#aliases). -
    - [...] -
    +```twig +
    + [...] +
    +``` Components can also reference their own properties. - {% for item in __SELF__.items() %} - {{ item }} - {% endfor %} +```twig +{% for item in __SELF__.items() %} + {{ item }} +{% endfor %} +``` If inside a component partial you need to render another component partial concatenate the `__SELF__` variable with the partial name: - {% partial __SELF__~"::screenshot-list" %} +```twig +{% partial __SELF__~"::screenshot-list" %} +``` ### Unique identifier If an identical component is called twice on the same page, an `id` property can be used to reference each instance. - {{__SELF__.id}} +```twig +{{__SELF__.id}} +``` The ID is unique each time the component is displayed. - - {% component 'demoTodo' %} +```twig + +{% component 'demoTodo' %} - - {% component 'demoTodo' %} + +{% component 'demoTodo' %} +``` ## Rendering partials from code You may programmatically render component partials inside the PHP code using the `renderPartial` method. This will check the component for the partial named `component-partial.htm` and return the result as a string. The second parameter is used for passing view variables. The same [path resolution logic](#component-partials) applies when you render a component partial in PHP as it does with Twig; use the `@` prefix to refer to partials within the component itself. - $content = $this->renderPartial('@component-partial.htm'); +```php +$content = $this->renderPartial('@component-partial.htm'); - $content = $this->renderPartial('@component-partial.htm', [ - 'name' => 'John Smith' - ]); +$content = $this->renderPartial('@component-partial.htm', [ + 'name' => 'John Smith' +]); +``` For example, to render a partial as a response to an [AJAX handler](../ajax/handlers): - function onGetTemplate() - { - return ['#someDiv' => $this->renderPartial('@component-partial.htm')]; - } +```php +function onGetTemplate() +{ + return ['#someDiv' => $this->renderPartial('@component-partial.htm')]; +} +``` Another example could be overriding the entire page view response by returning a value from the `onRun` [page cycle method](#page-cycle). This code will specifically return an XML response using the `Response` facade: - public function onRun() - { - $content = $this->renderPartial('@default.htm'); - return Response::make($content)->header('Content-Type', 'text/xml'); - } +```php +public function onRun() +{ + $content = $this->renderPartial('@default.htm'); + return Response::make($content)->header('Content-Type', 'text/xml'); +} +``` ## Injecting page assets with components diff --git a/plugin-extending.md b/plugin-extending.md index 379472c0..4a2f328a 100644 --- a/plugin-extending.md +++ b/plugin-extending.md @@ -25,20 +25,24 @@ The [Event service](../services/events) is the primary way to inject or modify t The most common place to subscribe to an event is the `boot` method of a [Plugin registration file](registration#registration-methods). For example, when a user is first registered you might want to add them to a third party mailing list, this could be achieved by subscribing to a `winter.user.register` global event. - public function boot() - { - Event::listen('winter.user.register', function ($user) { - // Code to register $user->email to mailing list - }); - } +```php +public function boot() +{ + Event::listen('winter.user.register', function ($user) { + // Code to register $user->email to mailing list + }); +} +``` The same can be achieved by extending the model's constructor and using a local event. - User::extend(function ($model) { - $model->bindEvent('user.register', function () use ($model) { - // Code to register $model->email to mailing list - }); +```php +User::extend(function ($model) { + $model->bindEvent('user.register', function () use ($model) { + // Code to register $model->email to mailing list }); +}); +``` ### Declaring / Firing events @@ -47,28 +51,36 @@ You can fire events globally (through the Event service) or locally. Local events are fired by calling `fireEvent()` on an instance of an object that implements `Winter\Storm\Support\Traits\Emitter`. Since local events are only fired on a specific object instance, it is not required to namespace them as it is less likely that a given project would have multiple events with the same name being fired on the same objects within a local context. - $this->fireEvent('post.beforePost', [$firstParam, $secondParam]); +```php +$this->fireEvent('post.beforePost', [$firstParam, $secondParam]); +``` Global events are fired by calling `Event::fire()`. As these events are global across the entire application, it is best practice to namespace them by including the vendor information in the name of the event. If your plugin Author is ACME and the plugin name is Blog, then any global events provided by the ACME.Blog plugin should be prefixed with `acme.blog`. - Event::fire('acme.blog.post.beforePost', [$firstParam, $secondParam]); +```php +Event::fire('acme.blog.post.beforePost', [$firstParam, $secondParam]); +``` If both global & local events are provided at the same place it's best practice to fire the local event before the global event so that the local event takes priority. Additionally, the global event should provide the object instance that the local event was fired on as the first parameter. - $this->fireEvent('post.beforePost', [$firstParam, $secondParam]); - Event::fire('winter.blog.beforePost', [$this, $firstParam, $secondParam]); +```php +$this->fireEvent('post.beforePost', [$firstParam, $secondParam]); +Event::fire('winter.blog.beforePost', [$this, $firstParam, $secondParam]); +``` Once this event has been subscribed to, the parameters are available in the handler method. For example: - // Global - Event::listen('acme.blog.post.beforePost', function ($post, $param1, $param2) { - Log::info($post->name . 'posted. Parameters: ' . $param1 . ' ' . $param2); - }); +```php +// Global +Event::listen('acme.blog.post.beforePost', function ($post, $param1, $param2) { + Log::info($post->name . 'posted. Parameters: ' . $param1 . ' ' . $param2); +}); - // Local - $post->bindEvent('post.beforePost', function ($param1, $param2) use ($post) { - Log::info($post->name . 'posted. Parameters: ' . $param1 . ' ' . $param2); - }); +// Local +$post->bindEvent('post.beforePost', function ($param1, $param2) use ($post) { + Log::info($post->name . 'posted. Parameters: ' . $param1 . ' ' . $param2); +}); +``` ## Extending backend views @@ -77,23 +89,29 @@ Sometimes you may wish to allow a backend view file or partial to be extended, s Place this code in your view file: - +```php + +``` This will allow other plugins to inject HTML to this area by hooking the event and returning the desired markup. - Event::listen('backend.auth.extendSigninView', function ($controller, $firstParam) { - return 'Sign in with Google!'; - }); +```php +Event::listen('backend.auth.extendSigninView', function ($controller, $firstParam) { + return 'Sign in with Google!'; +}); +``` > **NOTE**: The first parameter in the event handler will always be the calling object (the controller). The above example would output the following markup: - +```html + +``` ## Usage examples @@ -105,37 +123,39 @@ These are some practical examples of how events can be used. This example will modify the [`model.getAttribute`](../events/event/model.beforeGetAttribute) event of the `User` model by binding to its local event. This is carried out inside the `boot` method of the [Plugin registration file](registration#routing-initialization). In both cases, when the `$model->foo` attribute is accessed it will return the value *bar*. - class Plugin extends PluginBase +```php +class Plugin extends PluginBase +{ + [...] + + public function boot() { - [...] + // Local event hook that affects all users + User::extend(function ($model) { + $model->bindEvent('model.getAttribute', function ($attribute, $value) { + if ($attribute === 'foo') { + return 'bar'; + } + }); + }); + + // Double event hook that affects user #2 only + User::extend(function ($model) { + $model->bindEvent('model.afterFetch', function () use ($model) { + if ($model->id !== 2) { + return; + } - public function boot() - { - // Local event hook that affects all users - User::extend(function ($model) { $model->bindEvent('model.getAttribute', function ($attribute, $value) { if ($attribute === 'foo') { return 'bar'; } }); }); - - // Double event hook that affects user #2 only - User::extend(function ($model) { - $model->bindEvent('model.afterFetch', function () use ($model) { - if ($model->id !== 2) { - return; - } - - $model->bindEvent('model.getAttribute', function ($attribute, $value) { - if ($attribute === 'foo') { - return 'bar'; - } - }); - }); - }); - } + }); } +} +``` ### Extending backend forms @@ -144,44 +164,46 @@ There are a number of ways to [extend backend forms](../backend/forms#extend-for This example will listen to the [`backend.form.extendFields`](../events/event/backend.form.extendFields) global event of the `Backend\Widget\Form` widget and inject some extra fields when the Form widget is being used to modify a user. This event is also subscribed inside the `boot` method of the [Plugin registration file](registration#routing-initialization). - class Plugin extends PluginBase - { - [...] - - public function boot() - { - // Extend all backend form usage - Event::listen('backend.form.extendFields', function($widget) { - // Only apply this listener when the Users controller is being used - if (!$widget->getController() instanceof \Winter\User\Controllers\Users) { - return; - } - - // Only apply this listener when the User model is being modified - if (!$widget->model instanceof \Winter\User\Models\User) { - return; - } +```php +class Plugin extends PluginBase +{ + [...] - // Only apply this listener when the Form widget in question is a root-level - // Form widget (not a repeater, nestedform, etc) - if ($widget->isNested) { - return; - } - - // Add an extra birthday field - $widget->addFields([ - 'birthday' => [ - 'label' => 'Birthday', - 'comment' => 'Select the users birthday', - 'type' => 'datepicker' - ] - ]); - - // Remove a Surname field - $widget->removeField('surname'); - }); - } + public function boot() + { + // Extend all backend form usage + Event::listen('backend.form.extendFields', function($widget) { + // Only apply this listener when the Users controller is being used + if (!$widget->getController() instanceof \Winter\User\Controllers\Users) { + return; + } + + // Only apply this listener when the User model is being modified + if (!$widget->model instanceof \Winter\User\Models\User) { + return; + } + + // Only apply this listener when the Form widget in question is a root-level + // Form widget (not a repeater, nestedform, etc) + if ($widget->isNested) { + return; + } + + // Add an extra birthday field + $widget->addFields([ + 'birthday' => [ + 'label' => 'Birthday', + 'comment' => 'Select the users birthday', + 'type' => 'datepicker' + ] + ]); + + // Remove a Surname field + $widget->removeField('surname'); + }); } +} +``` > **NOTE:** In some cases (adding fields that should be made translatable by [Winter.Translate](https://github.com/wintercms/wn-translate-plugin) for example), you may want to extend the [`backend.form.extendFieldsBefore`](../events/event/backend.form.extendFieldsBefore) event instead. @@ -190,106 +212,116 @@ This example will listen to the [`backend.form.extendFields`](../events/event/ba This example will modify the [`backend.list.extendColumns`](../events/event/backend.list.extendColumns) global event of the `Backend\Widget\Lists` class and inject some extra columns values under the conditions that the list is being used to modify a user. This event is also subscribed inside the `boot` method of the [Plugin registration file](registration#routing-initialization). - class Plugin extends PluginBase - { - [...] - - public function boot() - { - // Extend all backend list usage - Event::listen('backend.list.extendColumns', function ($widget) { - // Only for the User controller - if (!$widget->getController() instanceof \Winter\User\Controllers\Users) { - return; - } - - // Only for the User model - if (!$widget->model instanceof \Winter\User\Models\User) { - return; - } +```php +class Plugin extends PluginBase +{ + [...] - // Add an extra birthday column - $widget->addColumns([ - 'birthday' => [ - 'label' => 'Birthday' - ], - ]); - - // Remove a Surname column - $widget->removeColumn('surname'); - }); - } + public function boot() + { + // Extend all backend list usage + Event::listen('backend.list.extendColumns', function ($widget) { + // Only for the User controller + if (!$widget->getController() instanceof \Winter\User\Controllers\Users) { + return; + } + + // Only for the User model + if (!$widget->model instanceof \Winter\User\Models\User) { + return; + } + + // Add an extra birthday column + $widget->addColumns([ + 'birthday' => [ + 'label' => 'Birthday' + ], + ]); + + // Remove a Surname column + $widget->removeColumn('surname'); + }); } +} +``` ### Extending a component This example will declare a new global event `winter.forum.topic.post` and local event called `topic.post` inside a `Topic` component. This is carried out in the [Component class definition](components#component-class-definition). - class Topic extends ComponentBase +```php +class Topic extends ComponentBase +{ + public function onPost() { - public function onPost() - { - [...] - - /* - * Extensibility - */ - $this->fireEvent('topic.post', [$post, $postUrl]); - Event::fire('winter.forum.topic.post', [$this, $post, $postUrl]); - } + [...] + + /* + * Extensibility + */ + $this->fireEvent('topic.post', [$post, $postUrl]); + Event::fire('winter.forum.topic.post', [$this, $post, $postUrl]); } +} +``` Next this will demonstrate how to hook to this new event from inside the [page execution life cycle](../cms/layouts#dynamic-pages). This will write to the trace log when the `onPost` event handler is called inside the `Topic` component (above). - [topic] - slug = "{{ :slug }}" - == - function onInit() - { - $this['topic']->bindEvent('topic.post', function($post, $postUrl) { - trace_log('A post has been submitted at '.$postUrl); - }); - } +```php +[topic] +slug = "{{ :slug }}" +== +function onInit() +{ + $this['topic']->bindEvent('topic.post', function($post, $postUrl) { + trace_log('A post has been submitted at '.$postUrl); + }); +} +``` ### Extending the backend menu This example will replace the label for CMS and Pages in the backend with *...*. - class Plugin extends PluginBase - { - [...] +```php +class Plugin extends PluginBase +{ + [...] - public function boot() - { - Event::listen('backend.menu.extendItems', function($manager) { + public function boot() + { + Event::listen('backend.menu.extendItems', function($manager) { - $manager->addMainMenuItems('Winter.Cms', [ - 'cms' => [ - 'label' => '...' - ] - ]); + $manager->addMainMenuItems('Winter.Cms', [ + 'cms' => [ + 'label' => '...' + ] + ]); - $manager->addSideMenuItems('Winter.Cms', 'cms', [ - 'pages' => [ - 'label' => '...' - ] - ]); + $manager->addSideMenuItems('Winter.Cms', 'cms', [ + 'pages' => [ + 'label' => '...' + ] + ]); - }); - } + }); } +} +``` Similarly we can remove the menu items with the same event: - Event::listen('backend.menu.extendItems', function($manager) { +```php +Event::listen('backend.menu.extendItems', function($manager) { - $manager->removeMainMenuItem('Winter.Cms', 'cms'); - $manager->removeSideMenuItem('Winter.Cms', 'cms', 'pages'); + $manager->removeMainMenuItem('Winter.Cms', 'cms'); + $manager->removeSideMenuItem('Winter.Cms', 'cms', 'pages'); - $manager->removeSideMenuItems('Winter.Cms', 'cms', [ - 'pages', - 'partials' - ]); - }); + $manager->removeSideMenuItems('Winter.Cms', 'cms', [ + 'pages', + 'partials' + ]); +}); +``` diff --git a/plugin-localization.md b/plugin-localization.md index 47f8ffd8..da132dd4 100644 --- a/plugin-localization.md +++ b/plugin-localization.md @@ -26,30 +26,36 @@ Below is an example of the plugin's lang directory: The **lang.php** file should define and return an array of any depth, for example: - [ - 'name' => 'Winter CMS', - 'tagline' => 'Getting back to basics' - ] - ]; +```php + [ + 'name' => 'Winter CMS', + 'tagline' => 'Getting back to basics' + ] +]; +``` The **validation.php** file has a similar structure to the **lang.php** and is used to specify your [custom validation](../services/validation#localization) messages in a language file, for example: - 'We need to know your xxx!', - 'email.required' => 'We need to know your e-mail address!', - ]; +return [ + 'required' => 'We need to know your xxx!', + 'email.required' => 'We need to know your e-mail address!', +]; +``` ## Accessing localization strings The localization strings can be loaded with the `Lang` class. The parameter it accepts is the localization key string that consists of the plugin name, the localization file name and the path to the localization string inside the array returned from the file. The next example loads the **app.name** string from the plugins/acme/blog/lang/en/lang.php file (the language is set with the `locale` parameter in the `config/app.php` configuration file): - echo Lang::get('acme.blog::lang.app.name'); +```php +echo Lang::get('acme.blog::lang.app.name'); +``` ## Overriding localization strings @@ -66,10 +72,12 @@ System users can override plugin localization strings without altering the plugi The file could contain only strings you want to override, there is no need to replace the entire file. Example: - [ - 'name' => 'Winter CMS!' - ] - ]; +return [ + 'app' => [ + 'name' => 'Winter CMS!' + ] +]; +``` diff --git a/plugin-registration.md b/plugin-registration.md index ee33b914..a7209acd 100644 --- a/plugin-registration.md +++ b/plugin-registration.md @@ -214,6 +214,12 @@ Method | Description The `pluginDetails` is a required method of the plugin registration class. It should return an array containing the following keys: + +
    + Key | Description ------------- | ------------- `name` | the plugin name, required. @@ -336,6 +342,14 @@ public function makeTextAllCaps($text) The following Twig custom options are available: + +
    + | Option | Type | Default | Description | | ------ | ---- | ------- | ----------- | | `needs_environment` | boolean | `false` | if true provides the current `TwigEnvironment` as the first argument to the filter call | @@ -395,6 +409,12 @@ When you register the backend navigation you can use [localization strings](loca To make the sub-menu items visible, you may [set the navigation context](../backend/controllers-ajax#navigation-context) in the backend controller using the `BackendMenu::setContext` method. This will make the parent menu item active and display the children in the side menu. + +
    + Key | Description ------------- | ------------- `label` | specifies the menu label localization string key, required. @@ -425,7 +445,7 @@ public function boot() Alternatively, you can push it directly into the Kernel via the following. -``` +```php public function boot() { // Add a new middleware to beginning of the stack. diff --git a/plugin-scheduling.md b/plugin-scheduling.md index ed6499ce..12bf947c 100644 --- a/plugin-scheduling.md +++ b/plugin-scheduling.md @@ -21,25 +21,31 @@ You may define all of your scheduled tasks by overriding the `registerSchedule` To get started, let's look at an example of scheduling a task. In this example, we will schedule a `Closure` to be called every day at midnight. Within the `Closure` we will execute a database query to clear a table: - class Plugin extends PluginBase +```php +class Plugin extends PluginBase +{ + [...] + + public function registerSchedule($schedule) { - [...] - - public function registerSchedule($schedule) - { - $schedule->call(function () { - \Db::table('recent_users')->delete(); - })->daily(); - } + $schedule->call(function () { + \Db::table('recent_users')->delete(); + })->daily(); } +} +``` In addition to scheduling `Closure` calls, you may also schedule [console commands](../console/commands) and operating system commands. For example, you may use the `command` method to schedule a console command: - $schedule->command('cache:clear')->daily(); +```php +$schedule->command('cache:clear')->daily(); +``` The `exec` command may be used to issue a command to the operating system: - $schedule->exec('node /home/acme/script.js')->daily(); +```php +$schedule->exec('node /home/acme/script.js')->daily(); +``` ### Schedule frequency options @@ -62,9 +68,11 @@ Method | Description These methods may be combined with additional constraints to create even more finely tuned schedules that only run on certain days of the week. For example, to schedule a command to run weekly on Monday: - $schedule->call(function () { - // Runs once a week on Monday at 13:00... - })->weekly()->mondays()->at('13:00'); +```php +$schedule->call(function () { + // Runs once a week on Monday at 13:00... +})->weekly()->mondays()->at('13:00'); +``` Below is a list of the additional schedule constraints: @@ -84,16 +92,20 @@ Method | Description The `when` method may be used to limit the execution of a task based on the result of a given truth test. In other words, if the given `Closure` return `true`, the task will execute as long as no other constraining conditions prevent the task from running: - $schedule->command('emails:send')->daily()->when(function () { - return true; - }); +```php +$schedule->command('emails:send')->daily()->when(function () { + return true; +}); +``` ### Preventing task overlaps By default, scheduled tasks will be run even if the previous instance of the task is still running. To prevent this, you may use the `withoutOverlapping` method: - $schedule->command('emails:send')->withoutOverlapping(); +```php +$schedule->command('emails:send')->withoutOverlapping(); +``` In this example, the `emails:send` [console command](../console/commands) will be run every minute if it is not already running. The `withoutOverlapping` method is especially useful if you have tasks that vary drastically in their execution time, preventing you from needing to predict exactly how long a given task will take. @@ -102,16 +114,20 @@ In this example, the `emails:send` [console command](../console/commands) will b The scheduler provides several convenient methods for working with the output generated by scheduled tasks. First, using the `sendOutputTo` method, you may send the output to a file for later inspection: - $schedule->command('emails:send') - ->daily() - ->sendOutputTo($filePath); +```php +$schedule->command('emails:send') + ->daily() + ->sendOutputTo($filePath); +``` Using the `emailOutputTo` method, you may e-mail the output to an e-mail address of your choice. Note that the output must first be sent to a file using the `sendOutputTo` method. Also before e-mailing the output of a task, you should configure [mail services](../services/mail): - $schedule->command('foo') - ->daily() - ->sendOutputTo($filePath) - ->emailOutputTo('foo@example.com'); +```php +$schedule->command('foo') + ->daily() + ->sendOutputTo($filePath) + ->emailOutputTo('foo@example.com'); +``` > **NOTE:** The `emailOutputTo` and `sendOutputTo` methods are exclusive to the `command` method and are not supported for `call`. @@ -120,22 +136,26 @@ Using the `emailOutputTo` method, you may e-mail the output to an e-mail address Using the `before` and `after` methods, you may specify code to be executed before and after the scheduled task is complete: - $schedule->command('emails:send') - ->daily() - ->before(function () { - // Task is about to start... - }) - ->after(function () { - // Task is complete... - }); +```php +$schedule->command('emails:send') + ->daily() + ->before(function () { + // Task is about to start... + }) + ->after(function () { + // Task is complete... + }); +``` #### Pinging URLs Using the `pingBefore` and `thenPing` methods, the scheduler can automatically ping a given URL before or after a task is complete. This method is useful for notifying an external service that your scheduled task is commencing or complete: - $schedule->command('emails:send') - ->daily() - ->pingBefore($url) - ->thenPing($url); +```php +$schedule->command('emails:send') + ->daily() + ->pingBefore($url) + ->thenPing($url); +``` > You need to install [Drivers plugin](https://wintercms.com/plugin/winter-drivers) before you can use either the `pingBefore($url)` or `thenPing($url)` features. diff --git a/plugin-settings.md b/plugin-settings.md index 96664540..539dbf77 100644 --- a/plugin-settings.md +++ b/plugin-settings.md @@ -21,20 +21,22 @@ You can create models for storing settings in the database by implementing the ` The settings model classes should extend the Model class and implement the `System.Behaviors.SettingsModel` behavior. The settings models, like any other models, should be defined in the **models** subdirectory of the plugin directory. The model from the next example should be defined in the `plugins/acme/demo/models/Settings.php` script. - 'ABCD']); +// Set an array of values +Settings::set(['api_key' => 'ABCD']); - // Set object values - $settings = Settings::instance(); - $settings->api_key = 'ABCD'; - $settings->save(); +// Set object values +$settings = Settings::instance(); +$settings->api_key = 'ABCD'; +$settings->save(); +``` ### Reading from a settings model The settings model has the static `get` method that enables you to load individual properties. Also, when you instantiate a model with the `instance` method, it loads the properties from the database and you can access them directly. - // Outputs: ABCD - echo Settings::instance()->api_key; - - // Get a single value - echo Settings::get('api_key'); +```php +// Outputs: ABCD +echo Settings::instance()->api_key; - // Get a value and return a default value if it doesn't exist - echo Settings::get('is_activated', true); +// Get a single value +echo Settings::get('api_key'); +// Get a value and return a default value if it doesn't exist +echo Settings::get('is_activated', true); +``` ## Backend settings pages @@ -97,40 +102,44 @@ The backend contains a dedicated area for housing settings and configuration, it The backend settings navigation links can be extended by overriding the `registerSettings` method inside the [Plugin registration class](registration#registration-file). When you create a configuration link you have two options - create a link to a specific backend page, or create a link to a settings model. The next example shows how to create a link to a backend page. - public function registerSettings() - { - return [ - 'location' => [ - 'label' => 'Locations', - 'description' => 'Manage available user countries and states.', - 'category' => 'Users', - 'icon' => 'icon-globe', - 'url' => Backend::url('acme/user/locations'), - 'order' => 500, - 'keywords' => 'geography place placement' - ] - ]; - } +```php +public function registerSettings() +{ + return [ + 'location' => [ + 'label' => 'Locations', + 'description' => 'Manage available user countries and states.', + 'category' => 'Users', + 'icon' => 'icon-globe', + 'url' => Backend::url('acme/user/locations'), + 'order' => 500, + 'keywords' => 'geography place placement' + ] + ]; +} +``` > **NOTE:** Backend settings pages should [set the settings context](#settings-page-context) in order to mark the corresponding settings menu item active in the System page sidebar. Settings context for settings models is detected automatically. The following example creates a link to a settings model. Settings models is a part of the settings API which is described above in the [Database settings](#database-settings) section. - public function registerSettings() - { - return [ - 'settings' => [ - 'label' => 'User Settings', - 'description' => 'Manage user based settings.', - 'category' => 'Users', - 'icon' => 'icon-cog', - 'class' => 'Acme\User\Models\Settings', - 'order' => 500, - 'keywords' => 'security location', - 'permissions' => ['acme.users.access_settings'] - ] - ]; - } +```php +public function registerSettings() +{ + return [ + 'settings' => [ + 'label' => 'User Settings', + 'description' => 'Manage user based settings.', + 'category' => 'Users', + 'icon' => 'icon-cog', + 'class' => 'Acme\User\Models\Settings', + 'order' => 500, + 'keywords' => 'security location', + 'permissions' => ['acme.users.access_settings'] + ] + ]; +} +``` The optional `keywords` parameter is used by the settings search feature. If keywords are not provided, the search uses only the settings item label and description. @@ -139,15 +148,17 @@ The optional `keywords` parameter is used by the settings search feature. If key Just like [setting navigation context in the controller](../backend/controllers-ajax#navigation-context), Backend settings pages should set the settings navigation context. It's required in order to mark the current settings link in the System page sidebar as active. Use the `System\Classes\SettingsManager` class to set the settings context. Usually it could be done in the controller constructor: - public function __construct() - { - parent::__construct(); +```php +public function __construct() +{ + parent::__construct(); - [...] + [...] - BackendMenu::setContext('Winter.System', 'system', 'settings'); - SettingsManager::setContext('You.Plugin', 'settings'); - } + BackendMenu::setContext('Winter.System', 'system', 'settings'); + SettingsManager::setContext('You.Plugin', 'settings'); +} +``` The first argument of the `setContext` method is the settings item owner in the following format: **author.plugin**. The second argument is the setting name, the same as you provided when [registering the backend settings page](#link-registration). @@ -156,20 +167,24 @@ The first argument of the `setContext` method is the settings item owner in the Plugins can have a configuration file `config.php` in the `config` subdirectory of the plugin directory. The configuration files are PHP scripts that define and return an **array**. Example configuration file `plugins/acme/demo/config/config.php`: - 10, - 'display' => 5 - ]; +return [ + 'maxItems' => 10, + 'display' => 5 +]; +``` Use the `Config` class for accessing the configuration values defined in the configuration file. The `Config::get($name, $default = null)` method accepts the plugin and the parameter name in the following format: **Acme.Demo::maxItems**. The second optional parameter defines the default value to return if the configuration parameter doesn't exist. Example: - use Config; +```php +use Config; - ... +... - $maxItems = Config::get('acme.demo::maxItems', 50); +$maxItems = Config::get('acme.demo::maxItems', 50); +``` A plugin configuration can be overridden by the application by creating a configuration file `config/author/plugin/config.php`, for example `config/acme/todo/config.php`, or `config/acme/todo/dev/config.php` for an environment specific override (in this case `dev`). @@ -177,11 +192,13 @@ A plugin configuration can be overridden by the application by creating a config Inside the overridden configuration file you can return only values you want to override: - 20 - ]; +return [ + 'maxItems' => 20 +]; +``` If you want to use separate configurations across different environments (eg: **dev**, **production**), simply create another file in `config/author/plugin/environment/config.php`. Replace **environment** with the environment name. This will be merged with `config/author/plugin/config.php`. @@ -189,10 +206,12 @@ Example: **config/author/plugin/production/config.php:** - 25 - ]; +return [ + 'maxItems' => 25 +]; +``` This will set `maxItems` to 25 when `APP_ENV` is set to **production**. diff --git a/services-application.md b/services-application.md index 935ab917..3e80998e 100644 --- a/services-application.md +++ b/services-application.md @@ -14,13 +14,17 @@ The inversion of control (IoC) container is a tool for managing class dependenci There are two ways the IoC container can resolve dependencies: via Closure callbacks or automatic resolution. First, we'll explore Closure callbacks. First, a "type" may be bound into the container: - App::bind('foo', function($app) { - return new FooBar; - }); +```php +App::bind('foo', function($app) { + return new FooBar; +}); +``` #### Resolving a type from the container - $value = App::make('foo'); +```php +$value = App::make('foo'); +``` When the `App::make` method is called, the Closure callback is executed and the result is returned. @@ -28,27 +32,35 @@ When the `App::make` method is called, the Closure callback is executed and the Sometimes you may wish to bind something into the container that should only be resolved once, and the same instance should be returned on subsequent calls into the container: - App::singleton('foo', function() { - return new FooBar; - }); +```php +App::singleton('foo', function() { + return new FooBar; +}); +``` #### Binding an existing instance into the container You may also bind an existing object instance into the container using the `instance` method: - $foo = new Foo; +```php +$foo = new Foo; - App::instance('foo', $foo); +App::instance('foo', $foo); +``` #### Binding an interface to an implementation In some cases, a class may depend on an interface implementation, not a "concrete type". When this is the case, the `App::bind` method must be used to inform the container which interface implementation to inject: - App::bind('UserRepositoryInterface', 'DbUserRepository'); +```php +App::bind('UserRepositoryInterface', 'DbUserRepository'); +``` Now consider the following code: - $users = App::make('UserRepositoryInterface'); +```php +$users = App::make('UserRepositoryInterface'); +``` Since we have bound the `UserRepositoryInterface` to a concrete type, the `DbUserRepository` will automatically be injected into this controller when it is created. @@ -68,27 +80,31 @@ In fact, [plugin registration files](../plugin/registration) inherit service pro To create a service provider, simply extend the `Winter\Storm\Support\ServiceProvider` class and define a `register` method: - use Winter\Storm\Support\ServiceProvider; +```php +use Winter\Storm\Support\ServiceProvider; - class FooServiceProvider extends ServiceProvider - { - - public function register() - { - $this->app->bind('foo', function() { - return new Foo; - }); - } +class FooServiceProvider extends ServiceProvider +{ + public function register() + { + $this->app->bind('foo', function() { + return new Foo; + }); } +} +``` + Note that in the `register` method, the application IoC container is available to you via the `$this->app` property. Once you have created a provider and are ready to register it with your application, simply add it to the `providers` array in your `app` configuration file. #### Registering a service provider at run-time You may also register a service provider at run-time using the `App::register` method: - App::register('FooServiceProvider'); +```php +App::register('FooServiceProvider'); +``` ## Application events @@ -97,29 +113,33 @@ You may also register a service provider at run-time using the `App::register` m You can register special events before a requested is routed using the `before` and `after` methods: - App::before(function ($request) { - // Code to execute before the request is routed - }); +```php +App::before(function ($request) { + // Code to execute before the request is routed +}); - App::after(function ($request) { - // Code to execute after the request is routed - }); +App::after(function ($request) { + // Code to execute after the request is routed +}); +``` #### Container events The service container fires an event each time it resolves an object. You may listen to this event using the `resolving` method: - App::resolving(function ($object, $app) { - // Called when container resolves object of any type... - }); +```php +App::resolving(function ($object, $app) { + // Called when container resolves object of any type... +}); - App::resolving('foo', function ($fooBar, $app) { - // Called when container resolves objects using hint "foo"... - }); +App::resolving('foo', function ($fooBar, $app) { + // Called when container resolves objects using hint "foo"... +}); - App::resolving('Acme\Blog\Classes\FooBar', function ($fooBar, $app) { - // Called when container resolves objects of type "FooBar"... - }); +App::resolving('Acme\Blog\Classes\FooBar', function ($fooBar, $app) { + // Called when container resolves objects of type "FooBar"... +}); +``` As you can see, the object being resolved will be passed to the callback, allowing you to set any additional properties on the object before it is given to its consumer. @@ -130,15 +150,21 @@ As you can see, the object being resolved will be passed to the callback, allowi You may use the `environment` method to discover the application environment as determined by the [environment configuration](../setup/configuration#environment-config). - // production - App::environment(); +```php +// production +App::environment(); +``` #### Determine the execution context It is possible to know if the current request is being performed in the administrative backend area using the `runningInBackend` method. - App::runningInBackend(); +```php +App::runningInBackend(); +``` You may also use the `runningInConsole` method to check if the executing code is taking place inside the [command line interface](../console/commands): - App::runningInConsole(); +```php +App::runningInConsole(); +``` diff --git a/services-asset-compilation.md b/services-asset-compilation.md index 5fb4593b..54cbb8cd 100644 --- a/services-asset-compilation.md +++ b/services-asset-compilation.md @@ -103,12 +103,14 @@ Symbol | Description The asset combiner supports common aliases that substitute file paths, these will begin with the `@` symbol. For example the [AJAX framework assets](../ajax/introduction#framework-script) can be included in the combiner: - +```twig + +``` The following aliases are supported: @@ -169,6 +171,7 @@ CombineAssets::registerCallback(function ($combiner) { $this->registerAlias('jquery', '~/modules/backend/assets/js/vendor/jquery-and-migrate.min.js'); }); ``` + ### Register Custom Asset Bundles diff --git a/services-collections.md b/services-collections.md index 998a3395..2101dac7 100644 --- a/services-collections.md +++ b/services-collections.md @@ -15,16 +15,18 @@ The `Winter\Storm\Support\Collection` class provides a fluent, convenient wrapper for working with arrays of data. For example, check out the following code. We'll create a new collection instance from the array, run the `strtoupper` function on each element, and then remove all empty elements: - $collection = new Winter\Storm\Support\Collection(['stewie', 'brian', null]); - - $collection = $collection - ->map(function ($name) { - return strtoupper($name); - }) - ->reject(function ($name) { - return empty($name); - }) - ; +```php +$collection = new Winter\Storm\Support\Collection(['stewie', 'brian', null]); + +$collection = $collection + ->map(function ($name) { + return strtoupper($name); + }) + ->reject(function ($name) { + return empty($name); + }) +; +``` The `Collection` class allows you to chain its methods to perform fluent mapping and reducing of the underlying array. In general every `Collection` method returns an entirely new `Collection` instance. @@ -33,7 +35,9 @@ The `Collection` class allows you to chain its methods to perform fluent mapping As described above, passing an array to the constructor of the `Winter\Storm\Support\Collection` class will return a new instance for the given array. So, creating a collection is as simple as: - $collection = new Winter\Storm\Support\Collection([1, 2, 3]); +```php +$collection = new Winter\Storm\Support\Collection([1, 2, 3]); +``` By default, collections of [database models](../database/model) are always returned as `Collection` instances; however, feel free to use the `Collection` class wherever it is convenient for your application. @@ -191,11 +195,13 @@ You may select any method from this table to see an example of its usage: The `all` method simply returns the underlying array represented by the collection: - $collection = new Collection([1, 2, 3]); +```php +$collection = new Collection([1, 2, 3]); - $collection->all(); +$collection->all(); - // [1, 2, 3] +// [1, 2, 3] +``` #### `average()` {#collection-method} @@ -207,92 +213,107 @@ Alias for the [`avg`](#method-avg) method. The `avg` method returns the [average value](https://en.wikipedia.org/wiki/Average) of a given key: - $average = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->avg('foo'); +```php +$average = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->avg('foo'); - // 20 +// 20 - $average = collect([1, 1, 2, 4])->avg(); +$average = collect([1, 1, 2, 4])->avg(); - // 2 +// 2 +``` #### `chunk()` {.collection-method} The `chunk` method breaks the collection into multiple, smaller collections of a given size: - $collection = new Collection([1, 2, 3, 4, 5, 6, 7]); +```php +$collection = new Collection([1, 2, 3, 4, 5, 6, 7]); - $chunks = $collection->chunk(4); +$chunks = $collection->chunk(4); - $chunks->toArray(); +$chunks->toArray(); - // [[1, 2, 3, 4], [5, 6, 7]] +// [[1, 2, 3, 4], [5, 6, 7]] +``` This method is especially useful in [CMS pages](../cms/pages) when working with a grid system, such as [Bootstrap](http://getbootstrap.com/css/#grid). Imagine you have a collection of models you want to display in a grid: - {% for chunk in products.chunk(3) %} -
    - {% for product in chunk %} -
    {{ product.name }}
    - {% endfor %} -
    - {% endfor %} +```twig +{% for chunk in products.chunk(3) %} +
    + {% for product in chunk %} +
    {{ product.name }}
    + {% endfor %} +
    +{% endfor %} +``` #### `collapse()` {.collection-method} The `collapse` method collapses a collection of arrays into a flat collection: - $collection = new Collection([[1, 2, 3], [4, 5, 6], [7, 8, 9]]); +```php +$collection = new Collection([[1, 2, 3], [4, 5, 6], [7, 8, 9]]); + +$collapsed = $collection->collapse(); - $collapsed = $collection->collapse(); +$collapsed->all(); - $collapsed->all(); +// [1, 2, 3, 4, 5, 6, 7, 8, 9] +``` - // [1, 2, 3, 4, 5, 6, 7, 8, 9] #### `combine()` {#collection-method} The `combine` method combines the values of the collection, as keys, with the values of another array or collection: - $collection = collect(['name', 'age']); +```php +$collection = collect(['name', 'age']); - $combined = $collection->combine(['George', 29]); +$combined = $collection->combine(['George', 29]); - $combined->all(); +$combined->all(); - // ['name' => 'George', 'age' => 29] +// ['name' => 'George', 'age' => 29] +``` #### `collect()` {#collection-method} The `collect` method returns a new `Collection` instance with the items currently in the collection: - $collectionA = collect([1, 2, 3]); +```php +$collectionA = collect([1, 2, 3]); - $collectionB = $collectionA->collect(); +$collectionB = $collectionA->collect(); - $collectionB->all(); +$collectionB->all(); - // [1, 2, 3] +// [1, 2, 3] +``` The `collect` method is primarily useful for converting [lazy collections](#lazy-collections) into standard `Collection` instances: - $lazyCollection = LazyCollection::make(function () { - yield 1; - yield 2; - yield 3; - }); +```php +$lazyCollection = LazyCollection::make(function () { + yield 1; + yield 2; + yield 3; +}); - $collection = $lazyCollection->collect(); +$collection = $lazyCollection->collect(); - get_class($collection); +get_class($collection); - // 'Illuminate\Support\Collection' +// 'Illuminate\Support\Collection' - $collection->all(); +$collection->all(); - // [1, 2, 3] +// [1, 2, 3] +``` > **Tip:** The `collect` method is especially useful when you have an instance of `Enumerable` and need a non-lazy collection instance. Since `collect()` is part of the `Enumerable` contract, you can safely use it to get a `Collection` instance. @@ -301,49 +322,57 @@ The `collect` method is primarily useful for converting [lazy collections](#lazy The `concat` method appends the given `array` or collection values onto the end of the collection: - $collection = collect(['John Doe']); +```php +$collection = collect(['John Doe']); - $concatenated = $collection->concat(['Jane Doe'])->concat(['name' => 'Johnny Doe']); +$concatenated = $collection->concat(['Jane Doe'])->concat(['name' => 'Johnny Doe']); - $concatenated->all(); +$concatenated->all(); - // ['John Doe', 'Jane Doe', 'Johnny Doe'] +// ['John Doe', 'Jane Doe', 'Johnny Doe'] +``` #### `contains()` {#collection-method} The `contains` method determines whether the collection contains a given item: - $collection = collect(['name' => 'Desk', 'price' => 100]); +```php +$collection = collect(['name' => 'Desk', 'price' => 100]); - $collection->contains('Desk'); +$collection->contains('Desk'); - // true +// true - $collection->contains('New York'); +$collection->contains('New York'); - // false +// false +``` You may also pass a key / value pair to the `contains` method, which will determine if the given pair exists in the collection: - $collection = collect([ - ['product' => 'Desk', 'price' => 200], - ['product' => 'Chair', 'price' => 100], - ]); +```php +$collection = collect([ + ['product' => 'Desk', 'price' => 200], + ['product' => 'Chair', 'price' => 100], +]); - $collection->contains('product', 'Bookcase'); +$collection->contains('product', 'Bookcase'); - // false +// false +``` Finally, you may also pass a callback to the `contains` method to perform your own truth test: - $collection = collect([1, 2, 3, 4, 5]); +```php +$collection = collect([1, 2, 3, 4, 5]); - $collection->contains(function ($value, $key) { - return $value > 5; - }); +$collection->contains(function ($value, $key) { + return $value > 5; +}); - // false +// false +``` The `contains` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`containsStrict`](#method-containsstrict) method to filter using "strict" comparisons. @@ -357,93 +386,103 @@ This method has the same signature as the [`contains`](#method-contains) method; The `count` method returns the total number of items in the collection: - $collection = new Collection([1, 2, 3, 4]); +```php +$collection = new Collection([1, 2, 3, 4]); - $collection->count(); +$collection->count(); - // 4 +// 4 +``` #### `countBy()` {#collection-method} The `countBy` method counts the occurrences of values in the collection. By default, the method counts the occurrences of every element: - $collection = collect([1, 2, 2, 2, 3]); +```php +$collection = collect([1, 2, 2, 2, 3]); - $counted = $collection->countBy(); +$counted = $collection->countBy(); - $counted->all(); +$counted->all(); - // [1 => 1, 2 => 3, 3 => 1] +// [1 => 1, 2 => 3, 3 => 1] +``` However, you pass a callback to the `countBy` method to count all items by a custom value: - $collection = collect(['alice@gmail.com', 'bob@yahoo.com', 'carlos@gmail.com']); +```php +$collection = collect(['alice@gmail.com', 'bob@yahoo.com', 'carlos@gmail.com']); - $counted = $collection->countBy(function ($email) { - return substr(strrchr($email, "@"), 1); - }); +$counted = $collection->countBy(function ($email) { + return substr(strrchr($email, "@"), 1); +}); - $counted->all(); +$counted->all(); - // ['gmail.com' => 2, 'yahoo.com' => 1] +// ['gmail.com' => 2, 'yahoo.com' => 1] +``` #### `crossJoin()` {#collection-method} The `crossJoin` method cross joins the collection's values among the given arrays or collections, returning a Cartesian product with all possible permutations: - $collection = collect([1, 2]); +```php +$collection = collect([1, 2]); - $matrix = $collection->crossJoin(['a', 'b']); +$matrix = $collection->crossJoin(['a', 'b']); - $matrix->all(); +$matrix->all(); - /* - [ - [1, 'a'], - [1, 'b'], - [2, 'a'], - [2, 'b'], - ] - */ +/* + [ + [1, 'a'], + [1, 'b'], + [2, 'a'], + [2, 'b'], + ] +*/ - $collection = collect([1, 2]); +$collection = collect([1, 2]); - $matrix = $collection->crossJoin(['a', 'b'], ['I', 'II']); +$matrix = $collection->crossJoin(['a', 'b'], ['I', 'II']); - $matrix->all(); +$matrix->all(); - /* - [ - [1, 'a', 'I'], - [1, 'a', 'II'], - [1, 'b', 'I'], - [1, 'b', 'II'], - [2, 'a', 'I'], - [2, 'a', 'II'], - [2, 'b', 'I'], - [2, 'b', 'II'], - ] - */ +/* + [ + [1, 'a', 'I'], + [1, 'a', 'II'], + [1, 'b', 'I'], + [1, 'b', 'II'], + [2, 'a', 'I'], + [2, 'a', 'II'], + [2, 'b', 'I'], + [2, 'b', 'II'], + ] +*/ +``` #### `dd()` {#collection-method} The `dd` method dumps the collection's items and ends execution of the script: - $collection = collect(['John Doe', 'Jane Doe']); +```php +$collection = collect(['John Doe', 'Jane Doe']); - $collection->dd(); +$collection->dd(); - /* - Collection { - #items: array:2 [ - 0 => "John Doe" - 1 => "Jane Doe" - ] - } - */ +/* + Collection { + #items: array:2 [ + 0 => "John Doe" + 1 => "Jane Doe" + ] + } +*/ +``` If you do not want to stop executing the script, use the [`dump`](#method-dump) method instead. @@ -452,77 +491,85 @@ If you do not want to stop executing the script, use the [`dump`](#method-dump) The `diff` method compares the collection against another collection or a plain PHP `array`: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $diff = $collection->diff([2, 4, 6, 8]); +$diff = $collection->diff([2, 4, 6, 8]); - $diff->all(); +$diff->all(); - // [1, 3, 5] +// [1, 3, 5] +``` #### `diffAssoc()` {#collection-method} The `diffAssoc` method compares the collection against another collection or a plain PHP `array` based on its keys and values. This method will return the key / value pairs in the original collection that are not present in the given collection: - $collection = collect([ - 'color' => 'orange', - 'type' => 'fruit', - 'remain' => 6 - ]); +```php +$collection = collect([ + 'color' => 'orange', + 'type' => 'fruit', + 'remain' => 6 +]); - $diff = $collection->diffAssoc([ - 'color' => 'yellow', - 'type' => 'fruit', - 'remain' => 3, - 'used' => 6, - ]); +$diff = $collection->diffAssoc([ + 'color' => 'yellow', + 'type' => 'fruit', + 'remain' => 3, + 'used' => 6, +]); - $diff->all(); +$diff->all(); - // ['color' => 'orange', 'remain' => 6] +// ['color' => 'orange', 'remain' => 6] +``` #### `diffKeys()` {#collection-method} The `diffKeys` method compares the collection against another collection or a plain PHP `array` based on its keys. This method will return the key / value pairs in the original collection that are not present in the given collection: - $collection = collect([ - 'one' => 10, - 'two' => 20, - 'three' => 30, - 'four' => 40, - 'five' => 50, - ]); +```php +$collection = collect([ + 'one' => 10, + 'two' => 20, + 'three' => 30, + 'four' => 40, + 'five' => 50, +]); - $diff = $collection->diffKeys([ - 'two' => 2, - 'four' => 4, - 'six' => 6, - 'eight' => 8, - ]); +$diff = $collection->diffKeys([ + 'two' => 2, + 'four' => 4, + 'six' => 6, + 'eight' => 8, +]); - $diff->all(); +$diff->all(); - // ['one' => 10, 'three' => 30, 'five' => 50] +// ['one' => 10, 'three' => 30, 'five' => 50] +``` #### `dump()` {#collection-method} The `dump` method dumps the collection's items: - $collection = collect(['John Doe', 'Jane Doe']); +```php +$collection = collect(['John Doe', 'Jane Doe']); - $collection->dump(); +$collection->dump(); - /* - Collection { - #items: array:2 [ - 0 => "John Doe" - 1 => "Jane Doe" - ] - } - */ +/* + Collection { + #items: array:2 [ + 0 => "John Doe" + 1 => "Jane Doe" + ] + } +*/ +``` If you want to stop executing the script after dumping the collection, use the [`dd`](#method-dd) method instead. @@ -531,23 +578,27 @@ If you want to stop executing the script after dumping the collection, use the [ The `duplicates` method retrieves and returns duplicate values from the collection: - $collection = collect(['a', 'b', 'a', 'c', 'b']); +```php +$collection = collect(['a', 'b', 'a', 'c', 'b']); - $collection->duplicates(); +$collection->duplicates(); - // [2 => 'a', 4 => 'b'] +// [2 => 'a', 4 => 'b'] +``` If the collection contains arrays or objects, you can pass the key of the attributes that you wish to check for duplicate values: - $employees = collect([ - ['email' => 'abigail@example.com', 'position' => 'Developer'], - ['email' => 'james@example.com', 'position' => 'Designer'], - ['email' => 'victoria@example.com', 'position' => 'Developer'], - ]) +```php +$employees = collect([ + ['email' => 'abigail@example.com', 'position' => 'Developer'], + ['email' => 'james@example.com', 'position' => 'Designer'], + ['email' => 'victoria@example.com', 'position' => 'Developer'], +]) - $employees->duplicates('position'); +$employees->duplicates('position'); - // [2 => 'Developer'] +// [2 => 'Developer'] +``` #### `duplicatesStrict()` {#collection-method} @@ -559,50 +610,59 @@ This method has the same signature as the [`duplicates`](#method-duplicates) met The `each` method iterates over the items in the collection and passes each item to a callback: - $collection->each(function ($item, $key) { - // - }); +```php +$collection->each(function ($item, $key) { + // +}); +``` If you would like to stop iterating through the items, you may return `false` from your callback: - $collection->each(function ($item, $key) { - if (/* some condition */) { - return false; - } - }); - +```php +$collection->each(function ($item, $key) { + if (/* some condition */) { + return false; + } +}); +``` #### `every()` {.collection-method} The `every` method creates a new collection consisting of every n-th element: - $collection = new Collection(['a', 'b', 'c', 'd', 'e', 'f']); +```php +$collection = new Collection(['a', 'b', 'c', 'd', 'e', 'f']); - $collection->every(4); +$collection->every(4); - // ['a', 'e'] +// ['a', 'e'] +``` You may optionally pass offset as the second argument: - $collection->every(4, 1); +```php +$collection->every(4, 1); - // ['b', 'f'] +// ['b', 'f'] +``` #### `filter()` {.collection-method} The `filter` method filters the collection by a given callback, keeping only those items that pass a given truth test: - $collection = new Collection([1, 2, 3, 4]); +```php +$collection = new Collection([1, 2, 3, 4]); - $filtered = $collection->filter(function ($item) { - return $item > 2; - }); +$filtered = $collection->filter(function ($item) { + return $item > 2; +}); - $filtered->all(); +$filtered->all(); - // [3, 4] +// [3, 4] +``` For the inverse of `filter`, see the [reject](#method-reject) method. @@ -611,103 +671,121 @@ For the inverse of `filter`, see the [reject](#method-reject) method. The `first` method returns the first element in the collection that passes a given truth test: - new Collection([1, 2, 3, 4])->first(function ($value, $key) { - return $value > 2; - }); +```php +new Collection([1, 2, 3, 4])->first(function ($value, $key) { + return $value > 2; +}); - // 3 +// 3 +``` You may also call the `first` method with no arguments to get the first element in the collection. If the collection is empty, `null` is returned: - new Collection([1, 2, 3, 4])->first(); +```php +new Collection([1, 2, 3, 4])->first(); - // 1 +// 1 +``` #### `firstWhere()` {#collection-method} The `firstWhere` method returns the first element in the collection with the given key / value pair: - $collection = collect([ - ['name' => 'Regena', 'age' => null], - ['name' => 'Linda', 'age' => 14], - ['name' => 'Diego', 'age' => 23], - ['name' => 'Linda', 'age' => 84], - ]); +```php +$collection = collect([ + ['name' => 'Regena', 'age' => null], + ['name' => 'Linda', 'age' => 14], + ['name' => 'Diego', 'age' => 23], + ['name' => 'Linda', 'age' => 84], +]); - $collection->firstWhere('name', 'Linda'); +$collection->firstWhere('name', 'Linda'); - // ['name' => 'Linda', 'age' => 14] +// ['name' => 'Linda', 'age' => 14] +``` You may also call the `firstWhere` method with an operator: - $collection->firstWhere('age', '>=', 18); +```php +$collection->firstWhere('age', '>=', 18); - // ['name' => 'Diego', 'age' => 23] +// ['name' => 'Diego', 'age' => 23] +``` Like the [where](#method-where) method, you may pass one argument to the `firstWhere` method. In this scenario, the `firstWhere` method will return the first item where the given item key's value is "truthy": - $collection->firstWhere('age'); +```php +$collection->firstWhere('age'); - // ['name' => 'Linda', 'age' => 14] +// ['name' => 'Linda', 'age' => 14] +``` #### `flatMap()` {#collection-method} The `flatMap` method iterates through the collection and passes each value to the given callback. The callback is free to modify the item and return it, thus forming a new collection of modified items. Then, the array is flattened by a level: - $collection = collect([ - ['name' => 'Sally'], - ['school' => 'Arkansas'], - ['age' => 28] - ]); +```php +$collection = collect([ + ['name' => 'Sally'], + ['school' => 'Arkansas'], + ['age' => 28] +]); - $flattened = $collection->flatMap(function ($values) { - return array_map('strtoupper', $values); - }); +$flattened = $collection->flatMap(function ($values) { + return array_map('strtoupper', $values); +}); - $flattened->all(); +$flattened->all(); - // ['name' => 'SALLY', 'school' => 'ARKANSAS', 'age' => '28']; +// ['name' => 'SALLY', 'school' => 'ARKANSAS', 'age' => '28']; +``` #### `flatten()` {.collection-method} The `flatten` method flattens a multi-dimensional collection into a single dimension: - $collection = new Collection(['name' => 'peter', 'languages' => ['php', 'javascript']]); +```php +$collection = new Collection(['name' => 'peter', 'languages' => ['php', 'javascript']]); - $flattened = $collection->flatten(); +$flattened = $collection->flatten(); - $flattened->all(); +$flattened->all(); - // ['peter', 'php', 'javascript']; +// ['peter', 'php', 'javascript']; +``` #### `flip()` {.collection-method} The `flip` method swaps the collection's keys with their corresponding values: - $collection = new Collection(['name' => 'peter', 'platform' => 'winter']); +```php +$collection = new Collection(['name' => 'peter', 'platform' => 'winter']); - $flipped = $collection->flip(); +$flipped = $collection->flip(); - $flipped->all(); +$flipped->all(); - // ['peter' => 'name', 'winter' => 'platform'] +// ['peter' => 'name', 'winter' => 'platform'] +``` #### `forget()` {.collection-method} The `forget` method removes an item from the collection by its key: - $collection = new Collection(['name' => 'peter', 'platform' => 'winter']); +```php +$collection = new Collection(['name' => 'peter', 'platform' => 'winter']); - $collection->forget('name'); +$collection->forget('name'); - $collection->all(); +$collection->all(); - // ['platform' => 'winter'] +// ['platform' => 'winter'] +``` > **NOTE:** Unlike most other collection methods, `forget` does not return a new modified collection; it modifies the collection it is called on. @@ -716,11 +794,13 @@ The `forget` method removes an item from the collection by its key: The `forPage` method returns a new collection containing the items that would be present on a given page number: - $collection = new Collection([1, 2, 3, 4, 5, 6, 7, 8, 9])->forPage(2, 3); +```php +$collection = new Collection([1, 2, 3, 4, 5, 6, 7, 8, 9])->forPage(2, 3); - $collection->all(); +$collection->all(); - // [4, 5, 6] +// [4, 5, 6] +``` The method requires the page number and the number of items to show per page, respectively. @@ -729,85 +809,97 @@ The method requires the page number and the number of items to show per page, re The `get` method returns the item at a given key. If the key does not exist, `null` is returned: - $collection = new Collection(['name' => 'peter', 'platform' => 'winter']); +```php +$collection = new Collection(['name' => 'peter', 'platform' => 'winter']); - $value = $collection->get('name'); +$value = $collection->get('name'); - // peter +// peter +``` You may optionally pass a default value as the second argument: - $collection = new Collection(['name' => 'peter', 'platform' => 'winter']); +```php +$collection = new Collection(['name' => 'peter', 'platform' => 'winter']); - $value = $collection->get('foo', 'default-value'); +$value = $collection->get('foo', 'default-value'); - // default-value +// default-value +``` You may even pass a callback as the default value. The result of the callback will be returned if the specified key does not exist: - $collection->get('email', function () { - return 'default-value'; - }); +```php +$collection->get('email', function () { + return 'default-value'; +}); - // default-value +// default-value +``` #### `groupBy()` {.collection-method} The `groupBy` method groups the collection's items by a given key: - $collection = new Collection([ - ['account_id' => 'account-x10', 'product' => 'Bookcase'], - ['account_id' => 'account-x10', 'product' => 'Chair'], - ['account_id' => 'account-x11', 'product' => 'Desk'], - ]); - - $grouped = $collection->groupBy('account_id'); - - $grouped->toArray(); - - /* - [ - 'account-x10' => [ - ['account_id' => 'account-x10', 'product' => 'Bookcase'], - ['account_id' => 'account-x10', 'product' => 'Chair'], - ], - 'account-x11' => [ - ['account_id' => 'account-x11', 'product' => 'Desk'], - ], - ] - */ +```php +$collection = new Collection([ + ['account_id' => 'account-x10', 'product' => 'Bookcase'], + ['account_id' => 'account-x10', 'product' => 'Chair'], + ['account_id' => 'account-x11', 'product' => 'Desk'], +]); + +$grouped = $collection->groupBy('account_id'); + +$grouped->toArray(); + +/* + [ + 'account-x10' => [ + ['account_id' => 'account-x10', 'product' => 'Bookcase'], + ['account_id' => 'account-x10', 'product' => 'Chair'], + ], + 'account-x11' => [ + ['account_id' => 'account-x11', 'product' => 'Desk'], + ], + ] +*/ +``` In addition to passing a string `key`, you may also pass a callback. The callback should return the value you wish to key the group by: - $grouped = $collection->groupBy(function ($item, $key) { - return substr($item['account_id'], -3); - }); - - $grouped->toArray(); - - /* - [ - 'x10' => [ - ['account_id' => 'account-x10', 'product' => 'Bookcase'], - ['account_id' => 'account-x10', 'product' => 'Chair'], - ], - 'x11' => [ - ['account_id' => 'account-x11', 'product' => 'Desk'], - ], - ] - */ +```php +$grouped = $collection->groupBy(function ($item, $key) { + return substr($item['account_id'], -3); +}); + +$grouped->toArray(); + +/* + [ + 'x10' => [ + ['account_id' => 'account-x10', 'product' => 'Bookcase'], + ['account_id' => 'account-x10', 'product' => 'Chair'], + ], + 'x11' => [ + ['account_id' => 'account-x11', 'product' => 'Desk'], + ], + ] +*/ +``` #### `has()` {.collection-method} The `has` method determines if a given key exists in the collection: - $collection = new Collection(['account_id' => 1, 'product' => 'Desk']); +```php +$collection = new Collection(['account_id' => 1, 'product' => 'Desk']); - $collection->has('email'); +$collection->has('email'); - // false +// false +``` #### `implode()` {.collection-method} @@ -816,33 +908,39 @@ The `implode` method joins the items in a collection. Its arguments depend on th If the collection contains arrays or objects, you should pass the key of the attributes you wish to join, and the "glue" string you wish to place between the values: - $collection = new Collection([ - ['account_id' => 1, 'product' => 'Chair'], - ['account_id' => 2, 'product' => 'Desk'], - ]); +```php +$collection = new Collection([ + ['account_id' => 1, 'product' => 'Chair'], + ['account_id' => 2, 'product' => 'Desk'], +]); - $collection->implode('product', ', '); +$collection->implode('product', ', '); - // Chair, Desk +// Chair, Desk +``` If the collection contains simple strings or numeric values, simply pass the "glue" as the only argument to the method: - new Collection([1, 2, 3, 4, 5])->implode('-'); +```php +new Collection([1, 2, 3, 4, 5])->implode('-'); - // '1-2-3-4-5' +// '1-2-3-4-5' +``` #### `intersect()` {.collection-method} The `intersect` method removes any values that are not present in the given `array` or collection: - $collection = new Collection(['Desk', 'Sofa', 'Chair']); +```php +$collection = new Collection(['Desk', 'Sofa', 'Chair']); - $intersect = $collection->intersect(['Desk', 'Chair', 'Bookcase']); +$intersect = $collection->intersect(['Desk', 'Chair', 'Bookcase']); - $intersect->all(); +$intersect->all(); - // [0 => 'Desk', 2 => 'Chair'] +// [0 => 'Desk', 2 => 'Chair'] +``` As you can see, the resulting collection will preserve the original collection's keys. @@ -851,133 +949,152 @@ As you can see, the resulting collection will preserve the original collection's The `intersectByKeys` method removes any keys from the original collection that are not present in the given `array` or collection: - $collection = collect([ - 'serial' => 'UX301', 'type' => 'screen', 'year' => 2009 - ]); +```php +$collection = collect([ + 'serial' => 'UX301', 'type' => 'screen', 'year' => 2009 +]); - $intersect = $collection->intersectByKeys([ - 'reference' => 'UX404', 'type' => 'tab', 'year' => 2011 - ]); +$intersect = $collection->intersectByKeys([ + 'reference' => 'UX404', 'type' => 'tab', 'year' => 2011 +]); - $intersect->all(); +$intersect->all(); - // ['type' => 'screen', 'year' => 2009] +// ['type' => 'screen', 'year' => 2009] +``` #### `isEmpty()` {.collection-method} The `isEmpty` method returns `true` if the collection is empty; otherwise `false` is returned: - new Collection([])->isEmpty(); +```php +new Collection([])->isEmpty(); - // true +// true +``` #### `isNotEmpty()` {#collection-method} The `isNotEmpty` method returns `true` if the collection is not empty; otherwise, `false` is returned: - collect([])->isNotEmpty(); +```php +collect([])->isNotEmpty(); - // false +// false +``` #### `join()` {#collection-method} The `join` method joins the collection's values with a string: - collect(['a', 'b', 'c'])->join(', '); // 'a, b, c' - collect(['a', 'b', 'c'])->join(', ', ', and '); // 'a, b, and c' - collect(['a', 'b'])->join(', ', ' and '); // 'a and b' - collect(['a'])->join(', ', ' and '); // 'a' - collect([])->join(', ', ' and '); // '' +```php +collect(['a', 'b', 'c'])->join(', '); // 'a, b, c' +collect(['a', 'b', 'c'])->join(', ', ', and '); // 'a, b, and c' +collect(['a', 'b'])->join(', ', ' and '); // 'a and b' +collect(['a'])->join(', ', ' and '); // 'a' +collect([])->join(', ', ' and '); // '' +``` #### `keyBy()` {.collection-method} Keys the collection by the given key: - $collection = new Collection([ - ['product_id' => 'prod-100', 'name' => 'chair'], - ['product_id' => 'prod-200', 'name' => 'desk'], - ]); +```php +$collection = new Collection([ + ['product_id' => 'prod-100', 'name' => 'chair'], + ['product_id' => 'prod-200', 'name' => 'desk'], +]); - $keyed = $collection->keyBy('product_id'); +$keyed = $collection->keyBy('product_id'); - $keyed->all(); +$keyed->all(); - /* - [ - 'prod-100' => ['product_id' => 'prod-100', 'name' => 'Chair'], - 'prod-200' => ['product_id' => 'prod-200', 'name' => 'Desk'], - ] - */ +/* + [ + 'prod-100' => ['product_id' => 'prod-100', 'name' => 'Chair'], + 'prod-200' => ['product_id' => 'prod-200', 'name' => 'Desk'], + ] +*/ +``` If multiple items have the same key, only the last one will appear in the new collection. You may also pass your own callback, which should return the value to key the collection by: - $keyed = $collection->keyBy(function ($item) { - return strtoupper($item['product_id']); - }); +```php +$keyed = $collection->keyBy(function ($item) { + return strtoupper($item['product_id']); +}); - $keyed->all(); - - /* - [ - 'PROD-100' => ['product_id' => 'prod-100', 'name' => 'Chair'], - 'PROD-200' => ['product_id' => 'prod-200', 'name' => 'Desk'], - ] - */ +$keyed->all(); +/* + [ + 'PROD-100' => ['product_id' => 'prod-100', 'name' => 'Chair'], + 'PROD-200' => ['product_id' => 'prod-200', 'name' => 'Desk'], + ] +*/ +``` #### `keys()` {.collection-method} The `keys` method returns all of the collection's keys: - $collection = new Collection([ - 'prod-100' => ['product_id' => 'prod-100', 'name' => 'Chair'], - 'prod-200' => ['product_id' => 'prod-200', 'name' => 'Desk'], - ]); +```php +$collection = new Collection([ + 'prod-100' => ['product_id' => 'prod-100', 'name' => 'Chair'], + 'prod-200' => ['product_id' => 'prod-200', 'name' => 'Desk'], +]); - $keys = $collection->keys(); +$keys = $collection->keys(); - $keys->all(); +$keys->all(); - // ['prod-100', 'prod-200'] +// ['prod-100', 'prod-200'] +``` #### `last()` {.collection-method} The `last` method returns the last element in the collection that passes a given truth test: - new Collection([1, 2, 3, 4])->last(function ($key, $value) { - return $value < 3; - }); +```php +new Collection([1, 2, 3, 4])->last(function ($key, $value) { + return $value < 3; +}); - // 2 +// 2 +``` You may also call the `last` method with no arguments to get the last element in the collection. If the collection is empty then `null` is returned. - new Collection([1, 2, 3, 4])->last(); +```php +new Collection([1, 2, 3, 4])->last(); - // 4 +// 4 +``` #### `map()` {.collection-method} The `map` method iterates through the collection and passes each value to the given callback. The callback is free to modify the item and return it, thus forming a new collection of modified items: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $multiplied = $collection->map(function ($item, $key) { - return $item * 2; - }); +$multiplied = $collection->map(function ($item, $key) { + return $item * 2; +}); - $multiplied->all(); +$multiplied->all(); - // [2, 4, 6, 8, 10] +// [2, 4, 6, 8, 10] +``` > **NOTE:** Like most other collection methods, `map` returns a new collection instance; it does not modify the collection it is called on. If you want to transform the original collection, use the [`transform`](#method-transform) method. @@ -986,230 +1103,258 @@ The `map` method iterates through the collection and passes each value to the gi The `mapInto()` method iterates over the collection, creating a new instance of the given class by passing the value into the constructor: - class Currency +```php +class Currency +{ + /** + * Create a new currency instance. + * + * @param string $code + * @return void + */ + function __construct(string $code) { - /** - * Create a new currency instance. - * - * @param string $code - * @return void - */ - function __construct(string $code) - { - $this->code = $code; - } + $this->code = $code; } +} - $collection = collect(['USD', 'EUR', 'GBP']); +$collection = collect(['USD', 'EUR', 'GBP']); - $currencies = $collection->mapInto(Currency::class); +$currencies = $collection->mapInto(Currency::class); - $currencies->all(); +$currencies->all(); - // [Currency('USD'), Currency('EUR'), Currency('GBP')] +// [Currency('USD'), Currency('EUR'), Currency('GBP')] +``` #### `mapSpread()` {#collection-method} The `mapSpread` method iterates over the collection's items, passing each nested item value into the given callback. The callback is free to modify the item and return it, thus forming a new collection of modified items: - $collection = collect([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); +```php +$collection = collect([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); - $chunks = $collection->chunk(2); +$chunks = $collection->chunk(2); - $sequence = $chunks->mapSpread(function ($even, $odd) { - return $even + $odd; - }); +$sequence = $chunks->mapSpread(function ($even, $odd) { + return $even + $odd; +}); - $sequence->all(); +$sequence->all(); - // [1, 5, 9, 13, 17] +// [1, 5, 9, 13, 17] +``` #### `mapToGroups()` {#collection-method} The `mapToGroups` method groups the collection's items by the given callback. The callback should return an associative array containing a single key / value pair, thus forming a new collection of grouped values: - $collection = collect([ - [ - 'name' => 'John Doe', - 'department' => 'Sales', - ], - [ - 'name' => 'Jane Doe', - 'department' => 'Sales', - ], - [ - 'name' => 'Johnny Doe', - 'department' => 'Marketing', - ] - ]); - - $grouped = $collection->mapToGroups(function ($item, $key) { - return [$item['department'] => $item['name']]; - }); - - $grouped->toArray(); - - /* - [ - 'Sales' => ['John Doe', 'Jane Doe'], - 'Marketing' => ['Johnny Doe'], - ] - */ - - $grouped->get('Sales')->all(); - - // ['John Doe', 'Jane Doe'] +```php +$collection = collect([ + [ + 'name' => 'John Doe', + 'department' => 'Sales', + ], + [ + 'name' => 'Jane Doe', + 'department' => 'Sales', + ], + [ + 'name' => 'Johnny Doe', + 'department' => 'Marketing', + ] +]); + +$grouped = $collection->mapToGroups(function ($item, $key) { + return [$item['department'] => $item['name']]; +}); + +$grouped->toArray(); + +/* + [ + 'Sales' => ['John Doe', 'Jane Doe'], + 'Marketing' => ['Johnny Doe'], + ] +*/ + +$grouped->get('Sales')->all(); + +// ['John Doe', 'Jane Doe'] +``` #### `mapWithKeys()` {#collection-method} The `mapWithKeys` method iterates through the collection and passes each value to the given callback. The callback should return an associative array containing a single key / value pair: - $collection = collect([ - [ - 'name' => 'John', - 'department' => 'Sales', - 'email' => 'john@example.com' - ], - [ - 'name' => 'Jane', - 'department' => 'Marketing', - 'email' => 'jane@example.com' - ] - ]); - - $keyed = $collection->mapWithKeys(function ($item) { - return [$item['email'] => $item['name']]; - }); - - $keyed->all(); - - /* - [ - 'john@example.com' => 'John', - 'jane@example.com' => 'Jane', - ] - */ +```php +$collection = collect([ + [ + 'name' => 'John', + 'department' => 'Sales', + 'email' => 'john@example.com' + ], + [ + 'name' => 'Jane', + 'department' => 'Marketing', + 'email' => 'jane@example.com' + ] +]); + +$keyed = $collection->mapWithKeys(function ($item) { + return [$item['email'] => $item['name']]; +}); + +$keyed->all(); + +/* + [ + 'john@example.com' => 'John', + 'jane@example.com' => 'Jane', + ] +*/ +``` #### `max()` {#collection-method} The `max` method returns the maximum value of a given key: - $max = collect([['foo' => 10], ['foo' => 20]])->max('foo'); +```php +$max = collect([['foo' => 10], ['foo' => 20]])->max('foo'); - // 20 +// 20 - $max = collect([1, 2, 3, 4, 5])->max(); +$max = collect([1, 2, 3, 4, 5])->max(); - // 5 +// 5 +``` #### `median()` {#collection-method} The `median` method returns the [median value](https://en.wikipedia.org/wiki/Median) of a given key: - $median = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->median('foo'); +```php +$median = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->median('foo'); - // 15 +// 15 - $median = collect([1, 1, 2, 4])->median(); +$median = collect([1, 1, 2, 4])->median(); - // 1.5 +// 1.5 +``` #### `merge()` {#collection-method} The `merge` method merges the given array or collection with the original collection. If a string key in the given items matches a string key in the original collection, the given items's value will overwrite the value in the original collection: - $collection = collect(['product_id' => 1, 'price' => 100]); +```php +$collection = collect(['product_id' => 1, 'price' => 100]); - $merged = $collection->merge(['price' => 200, 'discount' => false]); +$merged = $collection->merge(['price' => 200, 'discount' => false]); - $merged->all(); +$merged->all(); - // ['product_id' => 1, 'price' => 200, 'discount' => false] +// ['product_id' => 1, 'price' => 200, 'discount' => false] +``` If the given items's keys are numeric, the values will be appended to the end of the collection: - $collection = collect(['Desk', 'Chair']); +```php +$collection = collect(['Desk', 'Chair']); - $merged = $collection->merge(['Bookcase', 'Door']); +$merged = $collection->merge(['Bookcase', 'Door']); - $merged->all(); +$merged->all(); - // ['Desk', 'Chair', 'Bookcase', 'Door'] +// ['Desk', 'Chair', 'Bookcase', 'Door'] +``` #### `mergeRecursive()` {#collection-method} The `mergeRecursive` method merges the given array or collection recursively with the original collection. If a string key in the given items matches a string key in the original collection, then the values for these keys are merged together into an array, and this is done recursively: - $collection = collect(['product_id' => 1, 'price' => 100]); +```php +$collection = collect(['product_id' => 1, 'price' => 100]); - $merged = $collection->mergeRecursive(['product_id' => 2, 'price' => 200, 'discount' => false]); +$merged = $collection->mergeRecursive(['product_id' => 2, 'price' => 200, 'discount' => false]); - $merged->all(); +$merged->all(); - // ['product_id' => [1, 2], 'price' => [100, 200], 'discount' => false] +// ['product_id' => [1, 2], 'price' => [100, 200], 'discount' => false] +``` #### `min()` {#collection-method} The `min` method returns the minimum value of a given key: - $min = collect([['foo' => 10], ['foo' => 20]])->min('foo'); +```php +$min = collect([['foo' => 10], ['foo' => 20]])->min('foo'); - // 10 +// 10 - $min = collect([1, 2, 3, 4, 5])->min(); +$min = collect([1, 2, 3, 4, 5])->min(); - // 1 +// 1 +``` #### `mode()` {#collection-method} The `mode` method returns the [mode value](https://en.wikipedia.org/wiki/Mode_(statistics)) of a given key: - $mode = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->mode('foo'); +```php +$mode = collect([['foo' => 10], ['foo' => 10], ['foo' => 20], ['foo' => 40]])->mode('foo'); - // [10] +// [10] - $mode = collect([1, 1, 2, 4])->mode(); +$mode = collect([1, 1, 2, 4])->mode(); - // [1] +// [1] +``` #### `nth()` {#collection-method} The `nth` method creates a new collection consisting of every n-th element: - $collection = collect(['a', 'b', 'c', 'd', 'e', 'f']); +```php +$collection = collect(['a', 'b', 'c', 'd', 'e', 'f']); - $collection->nth(4); +$collection->nth(4); - // ['a', 'e'] +// ['a', 'e'] +``` You may optionally pass an offset as the second argument: - $collection->nth(4, 1); +```php +$collection->nth(4, 1); - // ['b', 'f'] +// ['b', 'f'] +``` #### `only()` {#collection-method} The `only` method returns the items in the collection with the specified keys: - $collection = collect(['product_id' => 1, 'name' => 'Desk', 'price' => 100, 'discount' => false]); +```php +$collection = collect(['product_id' => 1, 'name' => 'Desk', 'price' => 100, 'discount' => false]); - $filtered = $collection->only(['product_id', 'name']); +$filtered = $collection->only(['product_id', 'name']); - $filtered->all(); +$filtered->all(); - // ['product_id' => 1, 'name' => 'Desk'] +// ['product_id' => 1, 'name' => 'Desk'] +``` For the inverse of `only`, see the [except](#method-except) method. @@ -1220,199 +1365,229 @@ The `pad` method will fill the array with the given value until the array reache To pad to the left, you should specify a negative size. No padding will take place if the absolute value of the given size is less than or equal to the length of the array: - $collection = collect(['A', 'B', 'C']); +```php +$collection = collect(['A', 'B', 'C']); - $filtered = $collection->pad(5, 0); +$filtered = $collection->pad(5, 0); - $filtered->all(); +$filtered->all(); - // ['A', 'B', 'C', 0, 0] +// ['A', 'B', 'C', 0, 0] - $filtered = $collection->pad(-5, 0); +$filtered = $collection->pad(-5, 0); - $filtered->all(); +$filtered->all(); - // [0, 0, 'A', 'B', 'C'] +// [0, 0, 'A', 'B', 'C'] +``` #### `partition()` {#collection-method} The `partition` method may be combined with the `list` PHP function to separate elements that pass a given truth test from those that do not: - $collection = collect([1, 2, 3, 4, 5, 6]); +```php +$collection = collect([1, 2, 3, 4, 5, 6]); - list($underThree, $equalOrAboveThree) = $collection->partition(function ($i) { - return $i < 3; - }); +list($underThree, $equalOrAboveThree) = $collection->partition(function ($i) { + return $i < 3; +}); - $underThree->all(); +$underThree->all(); - // [1, 2] +// [1, 2] - $equalOrAboveThree->all(); +$equalOrAboveThree->all(); - // [3, 4, 5, 6] +// [3, 4, 5, 6] +``` #### `pipe()` {#collection-method} The `pipe` method passes the collection to the given callback and returns the result: - $collection = collect([1, 2, 3]); +```php +$collection = collect([1, 2, 3]); - $piped = $collection->pipe(function ($collection) { - return $collection->sum(); - }); +$piped = $collection->pipe(function ($collection) { + return $collection->sum(); +}); - // 6 +// 6 +``` #### `pluck()` {.collection-method} The `pluck` method retrieves all of the collection values for a given key: - $collection = new Collection([ - ['product_id' => 'prod-100', 'name' => 'Chair'], - ['product_id' => 'prod-200', 'name' => 'Desk'], - ]); +```php +$collection = new Collection([ + ['product_id' => 'prod-100', 'name' => 'Chair'], + ['product_id' => 'prod-200', 'name' => 'Desk'], +]); - $plucked = $collection->pluck('name'); +$plucked = $collection->pluck('name'); - $plucked->all(); +$plucked->all(); - // ['Chair', 'Desk'] +// ['Chair', 'Desk'] +``` You may also specify how you wish the resulting collection to be keyed: - $plucked = $collection->pluck('name', 'product_id'); +```php +$plucked = $collection->pluck('name', 'product_id'); - $plucked->all(); +$plucked->all(); - // ['prod-100' => 'Desk', 'prod-200' => 'Chair'] +// ['prod-100' => 'Desk', 'prod-200' => 'Chair'] +``` #### `pop()` {.collection-method} The `pop` method removes and returns the last item from the collection: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $collection->pop(); +$collection->pop(); - // 5 +// 5 - $collection->all(); +$collection->all(); - // [1, 2, 3, 4] +// [1, 2, 3, 4] +``` #### `prepend()` {.collection-method} The `prepend` method adds an item to the beginning of the collection: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $collection->prepend(0); +$collection->prepend(0); - $collection->all(); +$collection->all(); - // [0, 1, 2, 3, 4, 5] +// [0, 1, 2, 3, 4, 5] +``` #### `pull()` {.collection-method} The `pull` method removes and returns an item from the collection by its key: - $collection = new Collection(['product_id' => 'prod-100', 'name' => 'Desk']); +```php +$collection = new Collection(['product_id' => 'prod-100', 'name' => 'Desk']); - $collection->pull('name'); +$collection->pull('name'); - // 'Desk' +// 'Desk' - $collection->all(); +$collection->all(); - // ['product_id' => 'prod-100'] +// ['product_id' => 'prod-100'] +``` #### `push()` {.collection-method} The `push` method appends an item to the end of the collection: - $collection = new Collection([1, 2, 3, 4]); +```php +$collection = new Collection([1, 2, 3, 4]); - $collection->push(5); +$collection->push(5); - $collection->all(); +$collection->all(); - // [1, 2, 3, 4, 5] +// [1, 2, 3, 4, 5] +``` #### `put()` {.collection-method} The `put` method sets the given key and value in the collection: - $collection = new Collection(['product_id' => 1, 'name' => 'Desk']); +```php +$collection = new Collection(['product_id' => 1, 'name' => 'Desk']); - $collection->put('price', 100); +$collection->put('price', 100); - $collection->all(); +$collection->all(); - // ['product_id' => 1, 'name' => 'Desk', 'price' => 100] +// ['product_id' => 1, 'name' => 'Desk', 'price' => 100] +``` #### `random()` {.collection-method} The `random` method returns a random item from the collection: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $collection->random(); +$collection->random(); - // 4 - (retrieved randomly) +// 4 - (retrieved randomly) +``` You may optionally pass an integer to `random`. If that integer is more than `1`, a collection of items is returned: - $random = $collection->random(3); +```php +$random = $collection->random(3); - $random->all(); +$random->all(); - // [2, 4, 5] - (retrieved randomly) +// [2, 4, 5] - (retrieved randomly) +``` #### `reduce()` {.collection-method} The `reduce` method reduces the collection to a single value, passing the result of each iteration into the subsequent iteration: - $collection = new Collection([1, 2, 3]); +```php +$collection = new Collection([1, 2, 3]); - $total = $collection->reduce(function ($carry, $item) { - return $carry + $item; - }); +$total = $collection->reduce(function ($carry, $item) { + return $carry + $item; +}); - // 6 +// 6 +``` The value for `$carry` on the first iteration is `null`; however, you may specify its initial value by passing a second argument to `reduce`: - $collection->reduce(function ($carry, $item) { - return $carry + $item; - }, 4); +```php +$collection->reduce(function ($carry, $item) { + return $carry + $item; +}, 4); - // 10 +// 10 +``` #### `reject()` {.collection-method} The `reject` method filters the collection using the given callback. The callback should return `true` for any items it wishes to remove from the resulting collection: - $collection = new Collection([1, 2, 3, 4]); +```php +$collection = new Collection([1, 2, 3, 4]); - $filtered = $collection->reject(function ($item) { - return $item > 2; - }); +$filtered = $collection->reject(function ($item) { + return $item > 2; +}); - $filtered->all(); +$filtered->all(); - // [1, 2] +// [1, 2] +``` For the inverse of the `reject` method, see the [`filter`](#method-filter) method. @@ -1421,127 +1596,148 @@ For the inverse of the `reject` method, see the [`filter`](#method-filter) metho The `replace` method behaves similarly to `merge`; however, in addition to overwriting matching items with string keys, the `replace` method will also overwrite items in the collection that have matching numeric keys: - $collection = collect(['Taylor', 'Abigail', 'James']); +```php +$collection = collect(['Taylor', 'Abigail', 'James']); - $replaced = $collection->replace([1 => 'Victoria', 3 => 'Finn']); +$replaced = $collection->replace([1 => 'Victoria', 3 => 'Finn']); - $replaced->all(); +$replaced->all(); - // ['Taylor', 'Victoria', 'James', 'Finn'] +// ['Taylor', 'Victoria', 'James', 'Finn'] +``` #### `replaceRecursive()` {#collection-method} This method works like `replace`, but it will recur into arrays and apply the same replacement process to the inner values: - $collection = collect(['Taylor', 'Abigail', ['James', 'Victoria', 'Finn']]); +```php +$collection = collect(['Taylor', 'Abigail', ['James', 'Victoria', 'Finn']]); - $replaced = $collection->replaceRecursive(['Charlie', 2 => [1 => 'King']]); +$replaced = $collection->replaceRecursive(['Charlie', 2 => [1 => 'King']]); - $replaced->all(); +$replaced->all(); - // ['Charlie', 'Abigail', ['James', 'King', 'Finn']] +// ['Charlie', 'Abigail', ['James', 'King', 'Finn']] +``` #### `reverse()` {.collection-method} The `reverse` method reverses the order of the collection's items: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $reversed = $collection->reverse(); +$reversed = $collection->reverse(); - $reversed->all(); +$reversed->all(); - // [5, 4, 3, 2, 1] +// [5, 4, 3, 2, 1] +``` #### `search()` {.collection-method} The `search` method searches the collection for the given value and returns its key if found. If the item is not found, `false` is returned. - $collection = new Collection([2, 4, 6, 8]); +```php +$collection = new Collection([2, 4, 6, 8]); - $collection->search(4); +$collection->search(4); - // 1 +// 1 +``` The search is done using a "loose" comparison. To use strict comparison, pass `true` as the second argument to the method: - $collection->search('4', true); +```php +$collection->search('4', true); - // false +// false +``` Alternatively, you may pass in your own callback to search for the first item that passes your truth test: - $collection->search(function ($item, $key) { - return $item > 5; - }); +```php +$collection->search(function ($item, $key) { + return $item > 5; +}); - // 2 +// 2 +``` #### `shift()` {.collection-method} The `shift` method removes and returns the first item from the collection: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $collection->shift(); +$collection->shift(); - // 1 +// 1 - $collection->all(); +$collection->all(); - // [2, 3, 4, 5] +// [2, 3, 4, 5] +``` #### `shuffle()` {.collection-method} The `shuffle` method randomly shuffles the items in the collection: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $shuffled = $collection->shuffle(); +$shuffled = $collection->shuffle(); - $shuffled->all(); - - // [3, 2, 5, 1, 4] (generated randomly) +$shuffled->all(); +// [3, 2, 5, 1, 4] (generated randomly) +``` #### `skip()` {#collection-method} The `skip` method returns a new collection, without the first given amount of items: - $collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +```php +$collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - $collection = $collection->skip(4); +$collection = $collection->skip(4); - $collection->all(); +$collection->all(); - // [5, 6, 7, 8, 9, 10] +// [5, 6, 7, 8, 9, 10] +``` #### `slice()` {#collection-method} The `slice` method returns a slice of the collection starting at the given index: - $collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); +```php +$collection = collect([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - $slice = $collection->slice(4); +$slice = $collection->slice(4); - $slice->all(); +$slice->all(); - // [5, 6, 7, 8, 9, 10] +// [5, 6, 7, 8, 9, 10] +``` If you would like to limit the size of the returned slice, pass the desired size as the second argument to the method: - $slice = $collection->slice(4, 2); +```php +$slice = $collection->slice(4, 2); - $slice->all(); +$slice->all(); - // [5, 6] +// [5, 6] +``` The returned slice will preserve keys by default. If you do not wish to preserve the original keys, you can use the [`values`](#method-values) method to reindex them. @@ -1555,13 +1751,15 @@ Alias for the [`contains`](#method-contains) method. The `sort` method sorts the collection: - $collection = new Collection([5, 3, 1, 2, 4]); +```php +$collection = new Collection([5, 3, 1, 2, 4]); - $sorted = $collection->sort(); +$sorted = $collection->sort(); - $sorted->values()->all(); +$sorted->values()->all(); - // [1, 2, 3, 4, 5] +// [1, 2, 3, 4, 5] +``` The sorted collection keeps the original array keys. In this example we used the [`values`](#method-values) method to reset the keys to consecutively numbered indexes. @@ -1574,47 +1772,51 @@ If your sorting needs are more advanced, you may pass a callback to `sort` with The `sortBy` method sorts the collection by the given key: - $collection = new Collection([ - ['name' => 'Desk', 'price' => 200], - ['name' => 'Chair', 'price' => 100], - ['name' => 'Bookcase', 'price' => 150], - ]); +```php +$collection = new Collection([ + ['name' => 'Desk', 'price' => 200], + ['name' => 'Chair', 'price' => 100], + ['name' => 'Bookcase', 'price' => 150], +]); - $sorted = $collection->sortBy('price'); +$sorted = $collection->sortBy('price'); - $sorted->values()->all(); +$sorted->values()->all(); - /* - [ - ['name' => 'Chair', 'price' => 100], - ['name' => 'Bookcase', 'price' => 150], - ['name' => 'Desk', 'price' => 200], - ] - */ +/* + [ + ['name' => 'Chair', 'price' => 100], + ['name' => 'Bookcase', 'price' => 150], + ['name' => 'Desk', 'price' => 200], + ] +*/ +``` The sorted collection keeps the original array keys. In this example we used the [`values`](#method-values) method to reset the keys to consecutively numbered indexes. You can also pass your own callback to determine how to sort the collection values: - $collection = new Collection([ - ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']], - ['name' => 'Chair', 'colors' => ['Black']], - ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']], - ]); +```php +$collection = new Collection([ + ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']], + ['name' => 'Chair', 'colors' => ['Black']], + ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']], +]); - $sorted = $collection->sortBy(function ($product, $key) { - return count($product['colors']); - }); +$sorted = $collection->sortBy(function ($product, $key) { + return count($product['colors']); +}); - $sorted->values()->all(); +$sorted->values()->all(); - /* - [ - ['name' => 'Chair', 'colors' => ['Black']], - ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']], - ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']], - ] - */ +/* + [ + ['name' => 'Chair', 'colors' => ['Black']], + ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']], + ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']], + ] +*/ +``` #### `sortByDesc()` {.collection-method} @@ -1626,23 +1828,25 @@ This method has the same signature as the [`sortBy`](#method-sortby) method, but The `sortKeys` method sorts the collection by the keys of the underlying associative array: - $collection = collect([ - 'id' => 22345, - 'first' => 'John', - 'last' => 'Doe', - ]); +```php +$collection = collect([ + 'id' => 22345, + 'first' => 'John', + 'last' => 'Doe', +]); - $sorted = $collection->sortKeys(); +$sorted = $collection->sortKeys(); - $sorted->all(); +$sorted->all(); - /* - [ - 'first' => 'John', - 'id' => 22345, - 'last' => 'Doe', - ] - */ +/* + [ + 'first' => 'John', + 'id' => 22345, + 'last' => 'Doe', + ] +*/ +``` #### `sortKeysDesc()` {#collection-method} @@ -1654,218 +1858,250 @@ This method has the same signature as the [`sortKeys`](#method-sortkeys) method, The `splice` method removes and returns a slice of items starting at the specified index: - $collection = collect([1, 2, 3, 4, 5]); +```php +$collection = collect([1, 2, 3, 4, 5]); - $chunk = $collection->splice(2); +$chunk = $collection->splice(2); - $chunk->all(); +$chunk->all(); - // [3, 4, 5] +// [3, 4, 5] - $collection->all(); +$collection->all(); - // [1, 2] +// [1, 2] +``` You may pass a second argument to limit the size of the resulting chunk: - $collection = collect([1, 2, 3, 4, 5]); +```php +$collection = collect([1, 2, 3, 4, 5]); - $chunk = $collection->splice(2, 1); +$chunk = $collection->splice(2, 1); - $chunk->all(); +$chunk->all(); - // [3] +// [3] - $collection->all(); +$collection->all(); - // [1, 2, 4, 5] +// [1, 2, 4, 5] +``` In addition, you can pass a third argument containing the new items to replace the items removed from the collection: - $collection = collect([1, 2, 3, 4, 5]); +```php +$collection = collect([1, 2, 3, 4, 5]); - $chunk = $collection->splice(2, 1, [10, 11]); +$chunk = $collection->splice(2, 1, [10, 11]); - $chunk->all(); +$chunk->all(); - // [3] +// [3] - $collection->all(); +$collection->all(); - // [1, 2, 10, 11, 4, 5] +// [1, 2, 10, 11, 4, 5] +``` #### `splice()` {.collection-method} The `splice` method removes and returns a slice of items starting at the specified index: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $chunk = $collection->splice(2); +$chunk = $collection->splice(2); - $chunk->all(); +$chunk->all(); - // [3, 4, 5] +// [3, 4, 5] - $collection->all(); +$collection->all(); - // [1, 2] +// [1, 2] +``` You may pass a second argument to limit the size of the resulting chunk: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $chunk = $collection->splice(2, 1); +$chunk = $collection->splice(2, 1); - $chunk->all(); +$chunk->all(); - // [3] +// [3] - $collection->all(); +$collection->all(); - // [1, 2, 4, 5] +// [1, 2, 4, 5] +``` In addition, you can pass a third argument containing the new items to replace the items removed from the collection: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $chunk = $collection->splice(2, 1, [10, 11]); +$chunk = $collection->splice(2, 1, [10, 11]); - $chunk->all(); +$chunk->all(); - // [3] +// [3] - $collection->all(); +$collection->all(); - // [1, 2, 10, 11, 4, 5] +// [1, 2, 10, 11, 4, 5] +``` #### `split()` {#collection-method} The `split` method breaks a collection into the given number of groups: - $collection = collect([1, 2, 3, 4, 5]); +```php +$collection = collect([1, 2, 3, 4, 5]); - $groups = $collection->split(3); +$groups = $collection->split(3); - $groups->toArray(); +$groups->toArray(); - // [[1, 2], [3, 4], [5]] +// [[1, 2], [3, 4], [5]] +``` #### `sum()` {.collection-method} The `sum` method returns the sum of all items in the collection: - new Collection([1, 2, 3, 4, 5])->sum(); +```php +new Collection([1, 2, 3, 4, 5])->sum(); - // 15 +// 15 +``` If the collection contains nested arrays or objects, you should pass a key to use for determining which values to sum: - $collection = new Collection([ - ['name' => 'JavaScript: The Good Parts', 'pages' => 176], - ['name' => 'JavaScript: The Definitive Guide', 'pages' => 1096], - ]); +```php +$collection = new Collection([ + ['name' => 'JavaScript: The Good Parts', 'pages' => 176], + ['name' => 'JavaScript: The Definitive Guide', 'pages' => 1096], +]); - $collection->sum('pages'); +$collection->sum('pages'); - // 1272 +// 1272 +``` In addition, you may pass your own callback to determine which values of the collection to sum: - $collection = new Collection([ - ['name' => 'Chair', 'colors' => ['Black']], - ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']], - ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']], - ]); +```php +$collection = new Collection([ + ['name' => 'Chair', 'colors' => ['Black']], + ['name' => 'Desk', 'colors' => ['Black', 'Mahogany']], + ['name' => 'Bookcase', 'colors' => ['Red', 'Beige', 'Brown']], +]); - $collection->sum(function ($product) { - return count($product['colors']); - }); +$collection->sum(function ($product) { + return count($product['colors']); +}); - // 6 +// 6 +``` #### `take()` {.collection-method} The `take` method returns a new collection with the specified number of items: - $collection = new Collection([0, 1, 2, 3, 4, 5]); +```php +$collection = new Collection([0, 1, 2, 3, 4, 5]); - $chunk = $collection->take(3); +$chunk = $collection->take(3); - $chunk->all(); +$chunk->all(); - // [0, 1, 2] +// [0, 1, 2] +``` You may also pass a negative integer to take the specified amount of items from the end of the collection: - $collection = new Collection([0, 1, 2, 3, 4, 5]); +```php +$collection = new Collection([0, 1, 2, 3, 4, 5]); - $chunk = $collection->take(-2); +$chunk = $collection->take(-2); - $chunk->all(); +$chunk->all(); - // [4, 5] +// [4, 5] +``` #### `tap()` {#collection-method} The `tap` method passes the collection to the given callback, allowing you to "tap" into the collection at a specific point and do something with the items while not affecting the collection itself: - collect([2, 4, 3, 1, 5]) - ->sort() - ->tap(function ($collection) { - Log::debug('Values after sorting', $collection->values()->toArray()); - }) - ->shift(); +```php +collect([2, 4, 3, 1, 5]) + ->sort() + ->tap(function ($collection) { + Log::debug('Values after sorting', $collection->values()->toArray()); + }) + ->shift(); - // 1 +// 1 +``` #### `times()` {#collection-method} The static `times` method creates a new collection by invoking the callback a given amount of times: - $collection = Collection::times(10, function ($number) { - return $number * 9; - }); +```php +$collection = Collection::times(10, function ($number) { + return $number * 9; +}); - $collection->all(); +$collection->all(); - // [9, 18, 27, 36, 45, 54, 63, 72, 81, 90] +// [9, 18, 27, 36, 45, 54, 63, 72, 81, 90] +``` This method can be useful when combined with factories to create Eloquent models: - $categories = Collection::times(3, function ($number) { - return factory(Category::class)->create(['name' => "Category No. $number"]); - }); +```php +$categories = Collection::times(3, function ($number) { + return factory(Category::class)->create(['name' => "Category No. $number"]); +}); - $categories->all(); +$categories->all(); - /* - [ - ['id' => 1, 'name' => 'Category No. 1'], - ['id' => 2, 'name' => 'Category No. 2'], - ['id' => 3, 'name' => 'Category No. 3'], - ] - */ +/* + [ + ['id' => 1, 'name' => 'Category No. 1'], + ['id' => 2, 'name' => 'Category No. 2'], + ['id' => 3, 'name' => 'Category No. 3'], + ] +*/ +``` #### `toArray()` {.collection-method} The `toArray` method converts the collection into a plain PHP `array`. If the collection's values are [database models](../database/model), the models will also be converted to arrays: - $collection = new Collection(['name' => 'Desk', 'price' => 200]); +```php +$collection = new Collection(['name' => 'Desk', 'price' => 200]); - $collection->toArray(); +$collection->toArray(); - /* - [ - ['name' => 'Desk', 'price' => 200], - ] - */ +/* + [ + ['name' => 'Desk', 'price' => 200], + ] +*/ +``` > **NOTE:** `toArray` also converts all of its nested objects to an array. If you want to get the underlying array as is, use the [`all`](#method-all) method instead. @@ -1874,26 +2110,30 @@ The `toArray` method converts the collection into a plain PHP `array`. If the co The `toJson` method converts the collection into JSON: - $collection = new Collection(['name' => 'Desk', 'price' => 200]); +```php +$collection = new Collection(['name' => 'Desk', 'price' => 200]); - $collection->toJson(); +$collection->toJson(); - // '{"name":"Desk","price":200}' +// '{"name":"Desk","price":200}' +``` #### `transform()` {.collection-method} The `transform` method iterates over the collection and calls the given callback with each item in the collection. The items in the collection will be replaced by the values returned by the callback: - $collection = new Collection([1, 2, 3, 4, 5]); +```php +$collection = new Collection([1, 2, 3, 4, 5]); - $collection->transform(function ($item, $key) { - return $item * 2; - }); +$collection->transform(function ($item, $key) { + return $item * 2; +}); - $collection->all(); +$collection->all(); - // [2, 4, 6, 8, 10] +// [2, 4, 6, 8, 10] +``` > **NOTE:** Unlike most other collection methods, `transform` modifies the collection itself. If you wish to create a new collection instead, use the [`map`](#method-map) method. @@ -1902,64 +2142,72 @@ The `transform` method iterates over the collection and calls the given callback The `union` method adds the given array to the collection. If the given array contains keys that are already in the original collection, the original collection's values will be preferred: - $collection = collect([1 => ['a'], 2 => ['b']]); +```php +$collection = collect([1 => ['a'], 2 => ['b']]); - $union = $collection->union([3 => ['c'], 1 => ['b']]); +$union = $collection->union([3 => ['c'], 1 => ['b']]); - $union->all(); +$union->all(); - // [1 => ['a'], 2 => ['b'], 3 => ['c']] +// [1 => ['a'], 2 => ['b'], 3 => ['c']] +``` #### `unique()` {#collection-method} The `unique` method returns all of the unique items in the collection. The returned collection keeps the original array keys, so in this example we'll use the [`values`](#method-values) method to reset the keys to consecutively numbered indexes: - $collection = collect([1, 1, 2, 2, 3, 4, 2]); +```php +$collection = collect([1, 1, 2, 2, 3, 4, 2]); - $unique = $collection->unique(); +$unique = $collection->unique(); - $unique->values()->all(); +$unique->values()->all(); - // [1, 2, 3, 4] +// [1, 2, 3, 4] +``` When dealing with nested arrays or objects, you may specify the key used to determine uniqueness: - $collection = collect([ - ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'], - ['name' => 'iPhone 5', 'brand' => 'Apple', 'type' => 'phone'], - ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'], - ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'], - ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'], - ]); +```php +$collection = collect([ + ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'], + ['name' => 'iPhone 5', 'brand' => 'Apple', 'type' => 'phone'], + ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'], + ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'], + ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'], +]); - $unique = $collection->unique('brand'); +$unique = $collection->unique('brand'); - $unique->values()->all(); +$unique->values()->all(); - /* - [ - ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'], - ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'], - ] - */ +/* + [ + ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'], + ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'], + ] +*/ +``` You may also pass your own callback to determine item uniqueness: - $unique = $collection->unique(function ($item) { - return $item['brand'].$item['type']; - }); +```php +$unique = $collection->unique(function ($item) { + return $item['brand'].$item['type']; +}); - $unique->values()->all(); +$unique->values()->all(); - /* - [ - ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'], - ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'], - ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'], - ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'], - ] - */ +/* + [ + ['name' => 'iPhone 6', 'brand' => 'Apple', 'type' => 'phone'], + ['name' => 'Apple Watch', 'brand' => 'Apple', 'type' => 'watch'], + ['name' => 'Galaxy S6', 'brand' => 'Samsung', 'type' => 'phone'], + ['name' => 'Galaxy Gear', 'brand' => 'Samsung', 'type' => 'watch'], + ] +*/ +``` The `unique` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`uniqueStrict`](#method-uniquestrict) method to filter using "strict" comparisons. @@ -1973,19 +2221,21 @@ This method has the same signature as the [`unique`](#method-unique) method; how The `unless` method will execute the given callback unless the first argument given to the method evaluates to `true`: - $collection = collect([1, 2, 3]); +```php +$collection = collect([1, 2, 3]); - $collection->unless(true, function ($collection) { - return $collection->push(4); - }); +$collection->unless(true, function ($collection) { + return $collection->push(4); +}); - $collection->unless(false, function ($collection) { - return $collection->push(5); - }); +$collection->unless(false, function ($collection) { + return $collection->push(5); +}); - $collection->all(); +$collection->all(); - // [1, 2, 3, 5] +// [1, 2, 3, 5] +``` For the inverse of `unless`, see the [`when`](#method-when) method. @@ -2004,58 +2254,63 @@ Alias for the [`whenEmpty`](#method-whenempty) method. The static `unwrap` method returns the collection's underlying items from the given value when applicable: - Collection::unwrap(collect('John Doe')); - - // ['John Doe'] +```php +Collection::unwrap(collect('John Doe')); - Collection::unwrap(['John Doe']); +// ['John Doe'] - // ['John Doe'] +Collection::unwrap(['John Doe']); - Collection::unwrap('John Doe'); +// ['John Doe'] - // 'John Doe' +Collection::unwrap('John Doe'); +// 'John Doe' +``` #### `values()` {.collection-method} The `values` method returns a new collection with the keys reset to consecutive integers: - $collection = new Collection([ - 10 => ['product' => 'Desk', 'price' => 200], - 11 => ['product' => 'Desk', 'price' => 200] - ]); +```php +$collection = new Collection([ + 10 => ['product' => 'Desk', 'price' => 200], + 11 => ['product' => 'Desk', 'price' => 200] +]); - $values = $collection->values(); +$values = $collection->values(); - $values->all(); +$values->all(); - /* - [ - 0 => ['product' => 'Desk', 'price' => 200], - 1 => ['product' => 'Desk', 'price' => 200], - ] - */ +/* + [ + 0 => ['product' => 'Desk', 'price' => 200], + 1 => ['product' => 'Desk', 'price' => 200], + ] +*/ +``` #### `when()` {#collection-method} The `when` method will execute the given callback when the first argument given to the method evaluates to `true`: - $collection = collect([1, 2, 3]); +```php +$collection = collect([1, 2, 3]); - $collection->when(true, function ($collection) { - return $collection->push(4); - }); +$collection->when(true, function ($collection) { + return $collection->push(4); +}); - $collection->when(false, function ($collection) { - return $collection->push(5); - }); +$collection->when(false, function ($collection) { + return $collection->push(5); +}); - $collection->all(); +$collection->all(); - // [1, 2, 3, 4] +// [1, 2, 3, 4] +``` For the inverse of `when`, see the [`unless`](#method-unless) method. @@ -2064,39 +2319,41 @@ For the inverse of `when`, see the [`unless`](#method-unless) method. The `whenEmpty` method will execute the given callback when the collection is empty: - $collection = collect(['michael', 'tom']); +```php +$collection = collect(['michael', 'tom']); - $collection->whenEmpty(function ($collection) { - return $collection->push('adam'); - }); +$collection->whenEmpty(function ($collection) { + return $collection->push('adam'); +}); - $collection->all(); +$collection->all(); - // ['michael', 'tom'] +// ['michael', 'tom'] - $collection = collect(); +$collection = collect(); - $collection->whenEmpty(function ($collection) { - return $collection->push('adam'); - }); +$collection->whenEmpty(function ($collection) { + return $collection->push('adam'); +}); - $collection->all(); +$collection->all(); - // ['adam'] +// ['adam'] - $collection = collect(['michael', 'tom']); +$collection = collect(['michael', 'tom']); - $collection->whenEmpty(function ($collection) { - return $collection->push('adam'); - }, function ($collection) { - return $collection->push('taylor'); - }); +$collection->whenEmpty(function ($collection) { + return $collection->push('adam'); +}, function ($collection) { + return $collection->push('taylor'); +}); - $collection->all(); +$collection->all(); - // ['michael', 'tom', 'taylor'] +// ['michael', 'tom', 'taylor'] +``` For the inverse of `whenEmpty`, see the [`whenNotEmpty`](#method-whennotempty) method. @@ -2105,39 +2362,41 @@ For the inverse of `whenEmpty`, see the [`whenNotEmpty`](#method-whennotempty) m The `whenNotEmpty` method will execute the given callback when the collection is not empty: - $collection = collect(['michael', 'tom']); +```php +$collection = collect(['michael', 'tom']); - $collection->whenNotEmpty(function ($collection) { - return $collection->push('adam'); - }); +$collection->whenNotEmpty(function ($collection) { + return $collection->push('adam'); +}); - $collection->all(); +$collection->all(); - // ['michael', 'tom', 'adam'] +// ['michael', 'tom', 'adam'] - $collection = collect(); +$collection = collect(); - $collection->whenNotEmpty(function ($collection) { - return $collection->push('adam'); - }); +$collection->whenNotEmpty(function ($collection) { + return $collection->push('adam'); +}); - $collection->all(); +$collection->all(); - // [] +// [] - $collection = collect(); +$collection = collect(); - $collection->whenNotEmpty(function ($collection) { - return $collection->push('adam'); - }, function ($collection) { - return $collection->push('taylor'); - }); +$collection->whenNotEmpty(function ($collection) { + return $collection->push('adam'); +}, function ($collection) { + return $collection->push('taylor'); +}); - $collection->all(); +$collection->all(); - // ['taylor'] +// ['taylor'] +``` For the inverse of `whenNotEmpty`, see the [`whenEmpty`](#method-whenempty) method. @@ -2146,44 +2405,48 @@ For the inverse of `whenNotEmpty`, see the [`whenEmpty`](#method-whenempty) meth The `where` method filters the collection by a given key / value pair: - $collection = collect([ - ['product' => 'Desk', 'price' => 200], - ['product' => 'Chair', 'price' => 100], - ['product' => 'Bookcase', 'price' => 150], - ['product' => 'Door', 'price' => 100], - ]); +```php +$collection = collect([ + ['product' => 'Desk', 'price' => 200], + ['product' => 'Chair', 'price' => 100], + ['product' => 'Bookcase', 'price' => 150], + ['product' => 'Door', 'price' => 100], +]); - $filtered = $collection->where('price', 100); +$filtered = $collection->where('price', 100); - $filtered->all(); +$filtered->all(); - /* - [ - ['product' => 'Chair', 'price' => 100], - ['product' => 'Door', 'price' => 100], - ] - */ +/* + [ + ['product' => 'Chair', 'price' => 100], + ['product' => 'Door', 'price' => 100], + ] +*/ +``` The `where` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`whereStrict`](#method-wherestrict) method to filter using "strict" comparisons. Optionally, you may pass a comparison operator as the second parameter. - $collection = collect([ - ['name' => 'Jim', 'deleted_at' => '2019-01-01 00:00:00'], - ['name' => 'Sally', 'deleted_at' => '2019-01-02 00:00:00'], - ['name' => 'Sue', 'deleted_at' => null], - ]); +```php +$collection = collect([ + ['name' => 'Jim', 'deleted_at' => '2019-01-01 00:00:00'], + ['name' => 'Sally', 'deleted_at' => '2019-01-02 00:00:00'], + ['name' => 'Sue', 'deleted_at' => null], +]); - $filtered = $collection->where('deleted_at', '!=', null); +$filtered = $collection->where('deleted_at', '!=', null); - $filtered->all(); +$filtered->all(); - /* - [ - ['name' => 'Jim', 'deleted_at' => '2019-01-01 00:00:00'], - ['name' => 'Sally', 'deleted_at' => '2019-01-02 00:00:00'], - ] - */ +/* + [ + ['name' => 'Jim', 'deleted_at' => '2019-01-01 00:00:00'], + ['name' => 'Sally', 'deleted_at' => '2019-01-02 00:00:00'], + ] +*/ +``` #### `whereStrict()` {#collection-method} @@ -2195,48 +2458,52 @@ This method has the same signature as the [`where`](#method-where) method; howev The `whereBetween` method filters the collection within a given range: - $collection = collect([ - ['product' => 'Desk', 'price' => 200], - ['product' => 'Chair', 'price' => 80], - ['product' => 'Bookcase', 'price' => 150], - ['product' => 'Pencil', 'price' => 30], - ['product' => 'Door', 'price' => 100], - ]); +```php +$collection = collect([ + ['product' => 'Desk', 'price' => 200], + ['product' => 'Chair', 'price' => 80], + ['product' => 'Bookcase', 'price' => 150], + ['product' => 'Pencil', 'price' => 30], + ['product' => 'Door', 'price' => 100], +]); - $filtered = $collection->whereBetween('price', [100, 200]); +$filtered = $collection->whereBetween('price', [100, 200]); - $filtered->all(); +$filtered->all(); - /* - [ - ['product' => 'Desk', 'price' => 200], - ['product' => 'Bookcase', 'price' => 150], - ['product' => 'Door', 'price' => 100], - ] - */ +/* + [ + ['product' => 'Desk', 'price' => 200], + ['product' => 'Bookcase', 'price' => 150], + ['product' => 'Door', 'price' => 100], + ] +*/ +``` #### `whereIn()` {#collection-method} The `whereIn` method filters the collection by a given key / value contained within the given array: - $collection = collect([ - ['product' => 'Desk', 'price' => 200], - ['product' => 'Chair', 'price' => 100], - ['product' => 'Bookcase', 'price' => 150], - ['product' => 'Door', 'price' => 100], - ]); +```php +$collection = collect([ + ['product' => 'Desk', 'price' => 200], + ['product' => 'Chair', 'price' => 100], + ['product' => 'Bookcase', 'price' => 150], + ['product' => 'Door', 'price' => 100], +]); - $filtered = $collection->whereIn('price', [150, 200]); +$filtered = $collection->whereIn('price', [150, 200]); - $filtered->all(); +$filtered->all(); - /* - [ - ['product' => 'Desk', 'price' => 200], - ['product' => 'Bookcase', 'price' => 150], - ] - */ +/* + [ + ['product' => 'Desk', 'price' => 200], + ['product' => 'Bookcase', 'price' => 150], + ] +*/ +``` The `whereIn` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`whereInStrict`](#method-whereinstrict) method to filter using "strict" comparisons. @@ -2250,67 +2517,73 @@ This method has the same signature as the [`whereIn`](#method-wherein) method; h The `whereInstanceOf` method filters the collection by a given class type: - use App\User; - use App\Post; +```php +use App\User; +use App\Post; - $collection = collect([ - new User, - new User, - new Post, - ]); +$collection = collect([ + new User, + new User, + new Post, +]); - $filtered = $collection->whereInstanceOf(User::class); +$filtered = $collection->whereInstanceOf(User::class); - $filtered->all(); +$filtered->all(); - // [App\User, App\User] +// [App\User, App\User] +``` #### `whereNotBetween()` {#collection-method} The `whereNotBetween` method filters the collection within a given range: - $collection = collect([ - ['product' => 'Desk', 'price' => 200], - ['product' => 'Chair', 'price' => 80], - ['product' => 'Bookcase', 'price' => 150], - ['product' => 'Pencil', 'price' => 30], - ['product' => 'Door', 'price' => 100], - ]); +```php +$collection = collect([ + ['product' => 'Desk', 'price' => 200], + ['product' => 'Chair', 'price' => 80], + ['product' => 'Bookcase', 'price' => 150], + ['product' => 'Pencil', 'price' => 30], + ['product' => 'Door', 'price' => 100], +]); - $filtered = $collection->whereNotBetween('price', [100, 200]); +$filtered = $collection->whereNotBetween('price', [100, 200]); - $filtered->all(); +$filtered->all(); - /* - [ - ['product' => 'Chair', 'price' => 80], - ['product' => 'Pencil', 'price' => 30], - ] - */ +/* + [ + ['product' => 'Chair', 'price' => 80], + ['product' => 'Pencil', 'price' => 30], + ] +*/ +``` #### `whereNotIn()` {#collection-method} The `whereNotIn` method filters the collection by a given key / value not contained within the given array: - $collection = collect([ - ['product' => 'Desk', 'price' => 200], - ['product' => 'Chair', 'price' => 100], - ['product' => 'Bookcase', 'price' => 150], - ['product' => 'Door', 'price' => 100], - ]); +```php +$collection = collect([ + ['product' => 'Desk', 'price' => 200], + ['product' => 'Chair', 'price' => 100], + ['product' => 'Bookcase', 'price' => 150], + ['product' => 'Door', 'price' => 100], +]); - $filtered = $collection->whereNotIn('price', [150, 200]); +$filtered = $collection->whereNotIn('price', [150, 200]); - $filtered->all(); +$filtered->all(); - /* - [ - ['product' => 'Chair', 'price' => 100], - ['product' => 'Door', 'price' => 100], - ] - */ +/* + [ + ['product' => 'Chair', 'price' => 100], + ['product' => 'Door', 'price' => 100], + ] +*/ +``` The `whereNotIn` method uses "loose" comparisons when checking item values, meaning a string with an integer value will be considered equal to an integer of the same value. Use the [`whereNotInStrict`](#method-wherenotinstrict) method to filter using "strict" comparisons. @@ -2324,80 +2597,87 @@ This method has the same signature as the [`whereNotIn`](#method-wherenotin) met The `whereNotNull` method filters items where the given key is not null: - $collection = collect([ - ['name' => 'Desk'], - ['name' => null], - ['name' => 'Bookcase'], - ]); +```php +$collection = collect([ + ['name' => 'Desk'], + ['name' => null], + ['name' => 'Bookcase'], +]); - $filtered = $collection->whereNotNull('name'); +$filtered = $collection->whereNotNull('name'); - $filtered->all(); +$filtered->all(); - /* - [ - ['name' => 'Desk'], - ['name' => 'Bookcase'], - ] - */ +/* + [ + ['name' => 'Desk'], + ['name' => 'Bookcase'], + ] +*/ +``` #### `whereNull()` {#collection-method} The `whereNull` method filters items where the given key is null: - $collection = collect([ - ['name' => 'Desk'], - ['name' => null], - ['name' => 'Bookcase'], - ]); - - $filtered = $collection->whereNull('name'); +```php +$collection = collect([ + ['name' => 'Desk'], + ['name' => null], + ['name' => 'Bookcase'], +]); - $filtered->all(); +$filtered = $collection->whereNull('name'); - /* - [ - ['name' => null], - ] - */ +$filtered->all(); +/* + [ + ['name' => null], + ] +*/ +``` #### `wrap()` {#collection-method} The static `wrap` method wraps the given value in a collection when applicable: - $collection = Collection::wrap('John Doe'); +```php +$collection = Collection::wrap('John Doe'); - $collection->all(); +$collection->all(); - // ['John Doe'] +// ['John Doe'] - $collection = Collection::wrap(['John Doe']); +$collection = Collection::wrap(['John Doe']); - $collection->all(); +$collection->all(); - // ['John Doe'] +// ['John Doe'] - $collection = Collection::wrap(collect('John Doe')); +$collection = Collection::wrap(collect('John Doe')); - $collection->all(); +$collection->all(); - // ['John Doe'] +// ['John Doe'] +``` #### `zip()` {#collection-method} The `zip` method merges together the values of the given array with the values of the original collection at the corresponding index: - $collection = collect(['Chair', 'Desk']); +```php +$collection = collect(['Chair', 'Desk']); - $zipped = $collection->zip([100, 200]); +$zipped = $collection->zip([100, 200]); - $zipped->all(); +$zipped->all(); - // [['Chair', 100], ['Desk', 200]] +// [['Chair', 100], ['Desk', 200]] +``` ## Higher Order Messages @@ -2406,15 +2686,19 @@ Collections also provide support for "higher order messages", which are short-cu Each higher order message can be accessed as a dynamic property on a collection instance. For instance, let's use the `each` higher order message to call a method on each object within a collection: - $users = User::where('votes', '>', 500)->get(); +```php +$users = User::where('votes', '>', 500)->get(); - $users->each->markAsVip(); +$users->each->markAsVip(); +``` Likewise, we can use the `sum` higher order message to gather the total number of "votes" for a collection of users: - $users = User::where('group', 'Development')->get(); +```php +$users = User::where('group', 'Development')->get(); - return $users->sum->votes; +return $users->sum->votes; +``` ## Lazy Collections @@ -2428,51 +2712,59 @@ To supplement the already powerful `Collection` class, the `LazyCollection` clas For example, imagine your application needs to process a multi-gigabyte log file while taking advantage of Winter's collection methods to parse the logs. Instead of reading the entire file into memory at once, lazy collections may be used to keep only a small part of the file in memory at a given time: - use App\LogEntry; - use Illuminate\Support\LazyCollection; +```php +use App\LogEntry; +use Illuminate\Support\LazyCollection; - LazyCollection::make(function () { - $handle = fopen('log.txt', 'r'); +LazyCollection::make(function () { + $handle = fopen('log.txt', 'r'); - while (($line = fgets($handle)) !== false) { - yield $line; - } - })->chunk(4)->map(function ($lines) { - return LogEntry::fromLines($lines); - })->each(function (LogEntry $logEntry) { - // Process the log entry... - }); + while (($line = fgets($handle)) !== false) { + yield $line; + } +})->chunk(4)->map(function ($lines) { + return LogEntry::fromLines($lines); +})->each(function (LogEntry $logEntry) { + // Process the log entry... +}); +``` Or, imagine you need to iterate through 10,000 Eloquent models. When using traditional Winter collections, all 10,000 Eloquent models must be loaded into memory at the same time: - $users = App\User::all()->filter(function ($user) { - return $user->id > 500; - }); +```php +$users = App\User::all()->filter(function ($user) { + return $user->id > 500; +}); +``` However, the query builder's `cursor` method returns a `LazyCollection` instance. This allows you to still only run a single query against the database but also only keep one Eloquent model loaded in memory at a time. In this example, the `filter` callback is not executed until we actually iterate over each user individually, allowing for a drastic reduction in memory usage: - $users = App\User::cursor()->filter(function ($user) { - return $user->id > 500; - }); +```php +$users = App\User::cursor()->filter(function ($user) { + return $user->id > 500; +}); - foreach ($users as $user) { - echo $user->id; - } +foreach ($users as $user) { + echo $user->id; +} +``` ### Creating Lazy Collections To create a lazy collection instance, you should pass a PHP generator function to the collection's `make` method: - use Illuminate\Support\LazyCollection; +```php +use Illuminate\Support\LazyCollection; - LazyCollection::make(function () { - $handle = fopen('log.txt', 'r'); +LazyCollection::make(function () { + $handle = fopen('log.txt', 'r'); - while (($line = fgets($handle)) !== false) { - yield $line; - } - }); + while (($line = fgets($handle)) !== false) { + yield $line; + } +}); +``` ### The Enumerable Contract @@ -2603,33 +2895,35 @@ In addition to the methods defined in the `Enumerable` contract, the `LazyCollec While the `each` method calls the given callback for each item in the collection right away, the `tapEach` method only calls the given callback as the items are being pulled out of the list one by one: - $lazyCollection = LazyCollection::times(INF)->tapEach(function ($value) { - dump($value); - }); +```php +$lazyCollection = LazyCollection::times(INF)->tapEach(function ($value) { + dump($value); +}); - // Nothing has been dumped so far... +// Nothing has been dumped so far... - $array = $lazyCollection->take(3)->all(); +$array = $lazyCollection->take(3)->all(); - // 1 - // 2 - // 3 +// 1 +// 2 +// 3 +``` #### `remember()` {#collection-method} The `remember` method returns a new lazy collection that will remember any values that have already been enumerated and will not retrieve them again when the collection is enumerated again: - $users = User::cursor()->remember(); - - // No query has been executed yet... - - $users->take(5)->all(); +```php +$users = User::cursor()->remember(); - // The query has been executed and the first 5 users have been hydrated from the database... +// No query has been executed yet... - $users->take(20)->all(); +$users->take(5)->all(); - // First 5 users come from the collection's cache... The rest are hydrated from the database... +// The query has been executed and the first 5 users have been hydrated from the database... +$users->take(20)->all(); +// First 5 users come from the collection's cache... The rest are hydrated from the database... +``` diff --git a/services-error-log.md b/services-error-log.md index d6c15083..51f8067e 100644 --- a/services-error-log.md +++ b/services-error-log.md @@ -34,24 +34,28 @@ The amount of error detail your application displays through the browser is cont For local development, you should set the `debug` value to `true`. In your production environment, this value should always be `false`. - /* - |-------------------------------------------------------------------------- - | Application Debug Mode - |-------------------------------------------------------------------------- - | - | When your application is in debug mode, detailed error messages with - | stack traces will be shown on every error that occurs within your - | application. If disabled, a simple generic error page is shown. - | - */ - - 'debug' => false, +```php +/* +|-------------------------------------------------------------------------- +| Application Debug Mode +|-------------------------------------------------------------------------- +| +| When your application is in debug mode, detailed error messages with +| stack traces will be shown on every error that occurs within your +| application. If disabled, a simple generic error page is shown. +| +*/ + +'debug' => false, +``` #### Log file modes Winter supports `single`, `daily`, `syslog` and `errorlog` logging modes. For example, if you wish to use daily log files instead of a single file, you should simply set the `log` value in your `config/app.php` configuration file: - 'log' => 'daily' +```php +'log' => 'daily' +``` ## Available exceptions @@ -63,7 +67,9 @@ Winter comes with several basic exception types out of the box. The `Winter\Storm\Exception\ApplicationException` class, aliased as `ApplicationException`, is the most common exception type that is used when a simple application condition has failed. - throw new ApplicationException('You must be logged in to do that!'); +```php +throw new ApplicationException('You must be logged in to do that!'); +``` The error message will be simplified and will never include any sensitive information like the php file and line number. @@ -72,7 +78,9 @@ The error message will be simplified and will never include any sensitive inform The `Winter\Storm\Exception\SystemException` class, aliased as `SystemException`, is used for errors that are critical to the system functioning and are always logged. - throw new SystemException('Unable to contact the mail server API'); +```php +throw new SystemException('Unable to contact the mail server API'); +``` When this exception is thrown a detailed error message is shown with the file and line number where it occurred. @@ -81,15 +89,19 @@ When this exception is thrown a detailed error message is shown with the file an The `Winter\Storm\Exception\ValidationException` class, aliased as `ValidationException`, is used for errors that relate directly to a form submission and an invalid field. The message should contain an array with fields and error messages. - throw new ValidationException(['username' => 'Sorry that username is already taken!']); +```php +throw new ValidationException(['username' => 'Sorry that username is already taken!']); +``` You can also pass an instance of the [validation service](validation). - $validation = Validator::make(...); +```php +$validation = Validator::make(...); - if ($validation->fails()) { - throw new ValidationException($validation); - } +if ($validation->fails()) { + throw new ValidationException($validation); +} +``` When this exception is thrown the [AJAX framework](../ajax/introduction) will provide this information in a usable format and focus the first invalid field. @@ -98,7 +110,9 @@ When this exception is thrown the [AJAX framework](../ajax/introduction) will pr The `Winter\Storm\Exception\AjaxException` class, aliased as `AjaxException`, is considered a "smart error" and will return the HTTP code 406. This allows them to pass response contents as if they were a successful response. - throw new AjaxException(['#flashMessages' => $this->renderPartial(...)]); +```php +throw new AjaxException(['#flashMessages' => $this->renderPartial(...)]); +``` When this exception is thrown the [AJAX framework](../ajax/introduction) will follow the standard error workflow but will also refresh specified partials. @@ -109,21 +123,27 @@ All exceptions are handled by the `Winter\Storm\Foundation\Exception\Handler` cl However, you may specify custom handlers if needed using the `App::error` method. Handlers are called based on the type-hint of the Exception they handle. For example, you may create a handler that only handles `RuntimeException` instances: - App::error(function(RuntimeException $exception) { - // Handle the exception... - }); +```php +App::error(function(RuntimeException $exception) { + // Handle the exception... +}); +``` If an exception handler returns a response, that response will be sent to the browser and no other error handlers will be called: - App::error(function(InvalidUserException $exception) { - return 'Sorry! Something is wrong with this account!'; - }); +```php +App::error(function(InvalidUserException $exception) { + return 'Sorry! Something is wrong with this account!'; +}); +``` To listen for PHP fatal errors, you may use the `App::fatal` method: - App::fatal(function($exception) { - // - }); +```php +App::fatal(function($exception) { + // +}); +``` If you have several exception handlers, they should be defined from most generic to most specific. So, for example, a handler that handles all exceptions of type `Exception` should be defined before a custom exception type such as `SystemException`. @@ -136,11 +156,15 @@ Error handler registrations, like [event handlers](events), generally fall under Some exceptions describe HTTP error codes from the server. For example, this may be a "page not found" error (404), an "unauthorized error" (401) or even a developer generated 500 error. In order to generate such a response from anywhere in your application, use the following: - App::abort(404); +```php +App::abort(404); +``` The `abort` method will immediately raise an exception which will be rendered by the exception handler. Optionally, you may provide the response text: - App::abort(403, 'Unauthorized action.'); +```php +App::abort(403, 'Unauthorized action.'); +``` This method may be used at any time during the request's lifecycle. @@ -154,51 +178,61 @@ By default any errors will be shown with a detailed error page containing the fi By default Winter is configured to create a single log file for your application which is stored in the `storage/logs` directory. You may write information to the logs using the `Log` facade: - $user = User::find(1); - Log::info('Showing user profile for user: '.$user->name); +```php +$user = User::find(1); +Log::info('Showing user profile for user: '.$user->name); +``` -The logger provides the eight logging levels defined in [RFC 5424](http://tools.ietf.org/html/rfc5424): **emergency**, **alert**, **critical**, **error**, **warning**, **notice**, **info** and **debug**. +The logger provides the eight logging levels defined in [RFC 5424](https://tools.ietf.org/html/rfc5424): `emergency`, `alert`, `critical`, `error`, `warning`, `notice`, `info` and `debug`. - Log::emergency($error); - Log::alert($error); - Log::critical($error); - Log::error($error); - Log::warning($error); - Log::notice($error); - Log::info($error); - Log::debug($error); +```php +Log::emergency($error); +Log::alert($error); +Log::critical($error); +Log::error($error); +Log::warning($error); +Log::notice($error); +Log::info($error); +Log::debug($error); +``` #### Contextual information An array of contextual data may also be passed to the log methods. This contextual data will be formatted and displayed with the log message: - Log::info('User failed to login.', ['id' => $user->id]); +```php +Log::info('User failed to login.', ['id' => $user->id]); +``` ### Helper functions There are some global helper methods available to make logging easier. The `trace_log` function is an alias for `Log::info` with support for using arrays and exceptions as the message. - // Write a string value - $val = 'Hello world'; - trace_log('The value is '.$val); +```php +// Write a string value +$val = 'Hello world'; +trace_log('The value is '.$val); - // Dump an array value - $val = ['Some', 'array', 'data']; - trace_log($val); +// Dump an array value +$val = ['Some', 'array', 'data']; +trace_log($val); - // Trace an exception - try { - // - } - catch (Exception $ex) { - trace_log($ex); - } +// Trace an exception +try { + // +} +catch (Exception $ex) { + trace_log($ex); +} +``` The `trace_sql` function enables database logging, when called it will log every command sent to the database. These records only appear in the `system.log` file and will not appear in the administration area log as this is stored in the database and would result in a feedback loop. - trace_sql(); +```php +trace_sql(); - Db::table('users')->count(); +Db::table('users')->count(); - // select count(*) as aggregate from users +// select count(*) as aggregate from users +``` diff --git a/services-filesystem-cdn.md b/services-filesystem-cdn.md index 201da2c3..5293c3df 100644 --- a/services-filesystem-cdn.md +++ b/services-filesystem-cdn.md @@ -24,6 +24,7 @@ Of course, you may configure as many disks as you like, and may even have multip #### The local driver When using the `local` driver, note that all file operations are relative to the `root` directory defined in your configuration file. By default, this value is set to the `storage/app` directory. Therefore, the following method would store a file in `storage/app/file.txt`: + ```php Storage::disk('local')->put('file.txt', 'Contents'); ``` @@ -38,6 +39,7 @@ Before using the S3 or Rackspace drivers, you will need to install [Drivers plug ### Obtaining disk instances The `Storage` facade may be used to interact with any of your configured disks. For example, you may use the `put` method on the facade to store an avatar on the default disk. If you call methods on the `Storage` facade without first calling the `disk` method, the method call will automatically be passed to the default disk: + ```php $user = User::find($id); @@ -47,95 +49,122 @@ Storage::put( ); ``` When using multiple disks, you may access a particular disk using the `disk` method on the `Storage` facade. Of course, you may continue to chain methods to execute methods on the disk: + ```php $disk = Storage::disk('s3'); $contents = Storage::disk('local')->get('file.jpg') ``` + ### Retrieving files The `get` method may be used to retrieve the contents of a given file. The raw string contents of the file will be returned by the method: + ```php $contents = Storage::get('file.jpg'); ``` + The `exists` method may be used to determine if a given file exists on the disk: + ```php $exists = Storage::disk('s3')->exists('file.jpg'); ``` + #### File meta information The `size` method may be used to get the size of the file in bytes: + ```php $size = Storage::size('file1.jpg'); ``` + The `lastModified` method returns the UNIX timestamp of the last time the file was modified: + ```php $time = Storage::lastModified('file1.jpg'); ``` + ### Storing files The `put` method may be used to store a file on disk. You may also pass a PHP `resource` to the `put` method, which will use Flysystem's underlying stream support. Using streams is greatly recommended when dealing with large files: + ```php Storage::put('file.jpg', $contents); Storage::put('file.jpg', $resource); ``` + The `copy` method may be used to copy an existing file to a new location on the disk: + ```php Storage::copy('old/file1.jpg', 'new/file1.jpg'); ``` + The `move` method may be used to move an existing file to a new location: + ```php Storage::move('old/file1.jpg', 'new/file1.jpg'); ``` + #### Prepending / appending to files The `prepend` and `append` methods allow you to easily insert content at the beginning or end of a file: + ```php Storage::prepend('file.log', 'Prepended Text'); Storage::append('file.log', 'Appended Text'); ``` + ### Deleting files The `delete` method accepts a single filename or an array of files to remove from the disk: + ```php Storage::delete('file.jpg'); Storage::delete(['file1.jpg', 'file2.jpg']); ``` + ### Directories #### Get all files within a directory The `files` method returns an array of all of the files in a given directory. If you would like to retrieve a list of all files within a given directory including all sub-directories, you may use the `allFiles` method: + ```php $files = Storage::files($directory); $files = Storage::allFiles($directory); ``` + #### Get all directories within a directory The `directories` method returns an array of all the directories within a given directory. Additionally, you may use the `allDirectories` method to get a list of all directories within a given directory and all of its sub-directories: + ```php $directories = Storage::directories($directory); // Recursive... $directories = Storage::allDirectories($directory); ``` + #### Create a directory The `makeDirectory` method will create the given directory, including any needed sub-directories: + ```php Storage::makeDirectory($directory); ``` + #### Delete a directory Finally, the `deleteDirectory` may be used to remove a directory, including all of its files, from the disk: + ```php Storage::deleteDirectory($directory); ``` diff --git a/services-hashing-encryption.md b/services-hashing-encryption.md index 947feb10..ebd9fc19 100644 --- a/services-hashing-encryption.md +++ b/services-hashing-encryption.md @@ -16,9 +16,11 @@ The `Hash` facade provides secure Bcrypt hashing for storing user passwords. Bcr You may hash a password by calling the `make` method on the `Hash` facade: - $user = new User; - $user->password = Hash::make('mypassword'); - $user->save(); +```php +$user = new User; +$user->password = Hash::make('mypassword'); +$user->save(); +``` Alternatively, models can implement the [Hashable trait](../database/traits#hashable) to automatically hash attributes. @@ -26,17 +28,21 @@ Alternatively, models can implement the [Hashable trait](../database/traits#hash The `check` method allows you to verify that a given plain-text string corresponds to a given hash. - if (Hash::check('plain-text', $hashedPassword)) { - // The passwords match... - } +```php +if (Hash::check('plain-text', $hashedPassword)) { + // The passwords match... +} +``` #### Checking if a password needs to be rehashed The `needsRehash` function allows you to determine if the work factor used by the hasher has changed since the password was hashed: - if (Hash::needsRehash($hashed)) { - $hashed = Hash::make('plain-text'); - } +```php +if (Hash::needsRehash($hashed)) { + $hashed = Hash::make('plain-text'); +} +``` ## Encryption @@ -45,19 +51,23 @@ You may encrypt a value using the `Crypt` facade. All encrypted values are encry For example, we may use the `encrypt` method to encrypt a secret and store it on a [database model](../database/model): - $user = new User; - $user->secret = Crypt::encrypt('shhh no telling'); - $user->save(); +```php +$user = new User; +$user->secret = Crypt::encrypt('shhh no telling'); +$user->save(); +``` #### Decrypting a value Of course, you may decrypt values using the `decrypt` method on the `Crypt` facade. If the value can not be properly decrypted, such as when the MAC is invalid, an `Illuminate\Contracts\Encryption\DecryptException` exception will be thrown: - use Illuminate\Contracts\Encryption\DecryptException; +```php +use Illuminate\Contracts\Encryption\DecryptException; - try { - $decrypted = Crypt::decrypt($encryptedValue); - } - catch (DecryptException $ex) { - // - } +try { + $decrypted = Crypt::decrypt($encryptedValue); +} +catch (DecryptException $ex) { + // +} +``` diff --git a/services-helpers.md b/services-helpers.md index ab3c08bd..f9d7e367 100644 --- a/services-helpers.md +++ b/services-helpers.md @@ -113,250 +113,289 @@ Winter includes a variety of "helper" PHP functions. Many of these functions are The `array_add` function adds a given key / value pair to the array if the given key doesn't already exist in the array: - $array = array_add(['name' => 'Desk'], 'price', 100); +```php +$array = array_add(['name' => 'Desk'], 'price', 100); - // ['name' => 'Desk', 'price' => 100] +// ['name' => 'Desk', 'price' => 100] +``` #### `array_divide()` {#collection-method} The `array_divide` function returns two arrays, one containing the keys, and the other containing the values of the original array: - list($keys, $values) = array_divide(['name' => 'Desk']); +```php +list($keys, $values) = array_divide(['name' => 'Desk']); - // $keys: ['name'] +// $keys: ['name'] - // $values: ['Desk'] +// $values: ['Desk'] +``` #### `array_dot()` {#collection-method} The `array_dot` function flattens a multi-dimensional array into a single level array that uses "dot" notation to indicate depth: - $array = array_dot(['foo' => ['bar' => 'baz']]); +```php +$array = array_dot(['foo' => ['bar' => 'baz']]); - // ['foo.bar' => 'baz']; +// ['foo.bar' => 'baz']; +``` #### `array_undot()` {#collection-method} The `array_undot` function is the counter-part to the `array_dot` method. It will convert a dot-notated array into a standard associative array: - $array = array_undot([ - 'foo.bar' => 'baz' - ]); - - // [ - // 'foo' => [ - // 'bar' => 'baz' - // ] - // ] +```php +$array = array_undot([ + 'foo.bar' => 'baz' +]); +// [ +// 'foo' => [ +// 'bar' => 'baz' +// ] +// ] +``` #### `array_except()` {#collection-method} The `array_except` method removes the given key / value pairs from the array: - $array = ['name' => 'Desk', 'price' => 100]; +```php +$array = ['name' => 'Desk', 'price' => 100]; - $array = array_except($array, ['price']); +$array = array_except($array, ['price']); - // ['name' => 'Desk'] +// ['name' => 'Desk'] +``` #### `array_first()` {#collection-method} The `array_first` method returns the first element of an array passing a given truth test: - $array = [100, 200, 300]; +```php +$array = [100, 200, 300]; - $value = array_first($array, function ($key, $value) { - return $value >= 150; - }); +$value = array_first($array, function ($key, $value) { + return $value >= 150; +}); - // 200 +// 200 +``` A default value may also be passed as the third parameter to the method. This value will be returned if no value passes the truth test: - $value = array_first($array, $callback, $default); +```php +$value = array_first($array, $callback, $default); +``` #### `array_flatten()` {#collection-method} The `array_flatten` method will flatten a multi-dimensional array into a single level. - $array = ['name' => 'Joe', 'languages' => ['PHP', 'Ruby']]; +```php +$array = ['name' => 'Joe', 'languages' => ['PHP', 'Ruby']]; - $array = array_flatten($array); +$array = array_flatten($array); - // ['Joe', 'PHP', 'Ruby']; +// ['Joe', 'PHP', 'Ruby']; +``` #### `array_forget()` {#collection-method} The `array_forget` method removes a given key / value pair from a deeply nested array using "dot" notation: - $array = ['products' => ['desk' => ['price' => 100]]]; +```php +$array = ['products' => ['desk' => ['price' => 100]]]; - array_forget($array, 'products.desk'); +array_forget($array, 'products.desk'); - // ['products' => []] +// ['products' => []] +``` #### `array_get()` {#collection-method} The `array_get` method retrieves a value from a deeply nested array using "dot" notation: - $array = ['products' => ['desk' => ['price' => 100]]]; +```php +$array = ['products' => ['desk' => ['price' => 100]]]; - $value = array_get($array, 'products.desk'); +$value = array_get($array, 'products.desk'); - // ['price' => 100] +// ['price' => 100] +``` The `array_get` function also accepts a default value, which will be returned if the specific key is not found: - $value = array_get($array, 'names.john', 'default'); +```php +$value = array_get($array, 'names.john', 'default'); +``` #### `array_only()` {#collection-method} The `array_only` method will return only the specified key / value pairs from the given array: - $array = ['name' => 'Desk', 'price' => 100, 'orders' => 10]; +```php +$array = ['name' => 'Desk', 'price' => 100, 'orders' => 10]; - $array = array_only($array, ['name', 'price']); +$array = array_only($array, ['name', 'price']); - // ['name' => 'Desk', 'price' => 100] +// ['name' => 'Desk', 'price' => 100] +``` #### `array_pluck()` {#collection-method} The `array_pluck` method will pluck a list of the given key / value pairs from the array: - $array = [ - ['developer' => ['name' => 'Brian']], - ['developer' => ['name' => 'Stewie']] - ]; +```php +$array = [ + ['developer' => ['name' => 'Brian']], + ['developer' => ['name' => 'Stewie']] +]; - $array = array_pluck($array, 'developer.name'); +$array = array_pluck($array, 'developer.name'); - // ['Brian', 'Stewie']; +// ['Brian', 'Stewie']; +``` #### `array_pull()` {#collection-method} The `array_pull` method returns and removes a key / value pair from the array: - $array = ['name' => 'Desk', 'price' => 100]; +```php +$array = ['name' => 'Desk', 'price' => 100]; - $name = array_pull($array, 'name'); +$name = array_pull($array, 'name'); - // $name: Desk +// $name: Desk - // $array: ['price' => 100] +// $array: ['price' => 100] +``` #### `array_set()` {#collection-method} The `array_set` method sets a value within a deeply nested array using "dot" notation: - $array = ['products' => ['desk' => ['price' => 100]]]; +```php +$array = ['products' => ['desk' => ['price' => 100]]]; - array_set($array, 'products.desk.price', 200); +array_set($array, 'products.desk.price', 200); - // ['products' => ['desk' => ['price' => 200]]] +// ['products' => ['desk' => ['price' => 200]]] +``` #### `array_sort()` {#collection-method} The `array_sort` method sorts the array by the results of the given Closure: - $array = [ - ['name' => 'Desk'], - ['name' => 'Chair'], - ]; +```php +$array = [ + ['name' => 'Desk'], + ['name' => 'Chair'], +]; - $array = array_values(array_sort($array, function ($value) { - return $value['name']; - })); +$array = array_values(array_sort($array, function ($value) { + return $value['name']; +})); - /* - [ - ['name' => 'Chair'], - ['name' => 'Desk'], - ] - */ +/* + [ + ['name' => 'Chair'], + ['name' => 'Desk'], + ] +*/ +``` #### `array_sort_recursive()` {#collection-method} The `array_sort_recursive` function recursively sorts the array using the `sort` function: - $array = [ +```php +$array = [ + [ + 'Brian', + 'Shannon', + 'Alec', + ], + [ + 'PHP', + 'Ruby', + 'JavaScript', + ], +]; + +$array = array_sort_recursive($array); + +/* + [ [ + 'Alec', 'Brian', 'Shannon', - 'Alec', ], [ + 'JavaScript', 'PHP', 'Ruby', - 'JavaScript', - ], + ] ]; - - $array = array_sort_recursive($array); - - /* - [ - [ - 'Alec', - 'Brian', - 'Shannon', - ], - [ - 'JavaScript', - 'PHP', - 'Ruby', - ] - ]; - */ +*/ +``` #### `array_where()` {#collection-method} The `array_where` function filters the array using the given Closure: - $array = [100, '200', 300, '400', 500]; +```php +$array = [100, '200', 300, '400', 500]; - $array = array_where($array, function ($value, $key) { - return is_string($value); - }); +$array = array_where($array, function ($value, $key) { + return is_string($value); +}); - // [1 => 200, 3 => 400] +// [1 => 200, 3 => 400] +``` #### `head()` {#collection-method} The `head` function simply returns the first element in the given array: - $array = [100, 200, 300]; +```php +$array = [100, 200, 300]; - $first = head($array); +$first = head($array); - // 100 +// 100 +``` #### `last()` {#collection-method} The `last` function returns the last element in the given array: - $array = [100, 200, 300]; +```php +$array = [100, 200, 300]; - $last = last($array); +$last = last($array); - // 300 +// 300 +``` ## Paths @@ -366,7 +405,9 @@ The `last` function returns the last element in the given array: Path prefix symbols can be used to create a dynamic path. For example, a path beginning with `~/` will create a path relative to the application: - list: ~/plugins/acme/pay/models/invoiceitem/columns.yaml +```yaml +list: ~/plugins/acme/pay/models/invoiceitem/columns.yaml +``` These symbols are supported for creating dynamic paths: @@ -380,113 +421,153 @@ Symbol | Description The `app_path` function returns the fully qualified path to the `app` directory: - $path = app_path(); +```php +$path = app_path(); +``` You may also use the `app_path` function to generate a fully qualified path to a given file relative to the application directory: - $path = app_path('Http/Controllers/Controller.php'); +```php +$path = app_path('Http/Controllers/Controller.php'); +``` #### `base_path()` {#collection-method} The `base_path` function returns the fully qualified path to the project root: - $path = base_path(); +```php +$path = base_path(); +``` You may also use the `base_path` function to generate a fully qualified path to a given file relative to the application directory: - $path = base_path('vendor/bin'); +```php +$path = base_path('vendor/bin'); +``` #### `config_path($path = '')` {#collection-method} The `config_path` function returns the fully qualified path to the application configuration directory: - $path = config_path(); +```php +$path = config_path(); +``` You may also use the `config_path` function to generate a fully qualified path to a given file relative to the config directory: - $path = config_path('dev/cms.php'); +```php +$path = config_path('dev/cms.php'); +``` #### `database_path()` {#collection-method} The `database_path` function returns the fully qualified path to the application's database directory: - $path = database_path(); +```php +$path = database_path(); +``` #### `media_path($path = '')` {#collection-method} The `media_path` function returns the fully qualified path to the application media directory: - $path = media_path(); +```php +$path = media_path(); +``` You may also use the `media_path` function to generate a fully qualified path to a given file relative to the media directory: - $path = media_path('images/myimage.png'); +```php +$path = media_path('images/myimage.png'); +``` #### `plugins_path($path = '')` {#collection-method} The `plugins_path` function returns the fully qualified path to the application plugin directory: - $path = plugins_path(); +```php +$path = plugins_path(); +``` You may also use the `plugins_path` function to generate a fully qualified path to a given file relative to the plugins directory: - $path = plugins_path('author/plugin/routes.php'); +```php +$path = plugins_path('author/plugin/routes.php'); +``` #### `public_path()` {#collection-method} The `public_path` function returns the fully qualified path to the `public` directory: - $path = public_path(); +```php +$path = public_path(); +``` #### `storage_path($path = '')` {#collection-method} The `storage_path` function returns the fully qualified path to the `storage` directory: - $path = storage_path(); +```php +$path = storage_path(); +``` You may also use the `storage_path` function to generate a fully qualified path to a given file relative to the storage directory: - $path = storage_path('app/file.txt'); +```php +$path = storage_path('app/file.txt'); +``` #### `temp_path($path = '')` {#collection-method} The `temp_path` function returns the fully qualified path to a writable directory for temporary files: - $path = temp_path(); +```php +$path = temp_path(); +``` You may also use the `temp_path` function to generate a fully qualified path to a given file relative to the temp directory: - $path = temp_path('app/file.txt'); +```php +$path = temp_path('app/file.txt'); +``` #### `themes_path($path = '')` {#collection-method} The `themes_path` function returns the fully qualified path to the `themes` directory: - $path = themes_path(); +```php +$path = themes_path(); +``` You may also use the `themes_path` function to generate a fully qualified path to a given file relative to the themes directory: - $path = themes_path('mytheme/file.txt'); +```php +$path = themes_path('mytheme/file.txt'); +``` #### `uploads_path($path = '')` {#collection-method} The `uploads_path` function returns the fully qualified path to the application uploads directory: - $path = uploads_path(); +```php +$path = uploads_path(); +``` You may also use the `uploads_path` function to generate a fully qualified path to a given file relative to the uploads directory: - $path = uploads_path('public/file.txt'); +```php +$path = uploads_path('public/file.txt'); +``` ## Strings @@ -496,155 +577,189 @@ You may also use the `uploads_path` function to generate a fully qualified path The `camel_case` function converts the given string to `camelCase`: - $camel = camel_case('foo_bar'); +```php +$camel = camel_case('foo_bar'); - // fooBar +// fooBar +``` #### `class_basename()` {#collection-method} The `class_basename` returns the class name of the given class with the class' namespace removed: - $class = class_basename('Foo\Bar\Baz'); +```php +$class = class_basename('Foo\Bar\Baz'); - // Baz +// Baz +``` #### `e()` {#collection-method} The `e` function runs `htmlentities` over the given string: - echo e('foo'); +```php +echo e('foo'); - // <html>foo</html> +// <html>foo</html> +``` #### `ends_with()` {#collection-method} The `ends_with` function determines if the given string ends with the given value: - $value = ends_with('This is my name', 'name'); +```php +$value = ends_with('This is my name', 'name'); - // true +// true +``` #### `snake_case()` {#collection-method} The `snake_case` function converts the given string to `snake_case`: - $snake = snake_case('fooBar'); +```php +$snake = snake_case('fooBar'); - // foo_bar +// foo_bar +``` #### `str_limit()` {#collection-method} The `str_limit` function limits the number of characters in a string. The function accepts a string as its first argument and the maximum number of resulting characters as its second argument: - $value = str_limit('The CMS platform that gets back to basics.', 6); +```php +$value = str_limit('The CMS platform that gets back to basics.', 6); - // The CMS... +// The CMS... +``` #### `starts_with()` {#collection-method} The `starts_with` function determines if the given string begins with the given value: - $value = starts_with('The cow goes moo', 'The'); +```php +$value = starts_with('The cow goes moo', 'The'); - // true +// true +``` #### `str_contains()` {#collection-method} The `str_contains` function determines if the given string contains the given value: - $value = str_contains('The bird goes tweet', 'bird'); +```php +$value = str_contains('The bird goes tweet', 'bird'); - // true +// true +``` #### `str_finish()` {#collection-method} The `str_finish` function adds a single instance of the given value to a string: - $string = str_finish('this/string', '/'); +```php +$string = str_finish('this/string', '/'); - // this/string/ +// this/string/ +``` #### `str_is()` {#collection-method} The `str_is` function determines if a given string matches a given pattern. Asterisks may be used to indicate wildcards: - $value = str_is('foo*', 'foobar'); +```php +$value = str_is('foo*', 'foobar'); - // true +// true - $value = str_is('baz*', 'foobar'); +$value = str_is('baz*', 'foobar'); - // false +// false +``` #### `str_plural()` {#collection-method} The `str_plural` function converts a string to its plural form. This function currently only supports the English language: - $plural = str_plural('car'); +```php +$plural = str_plural('car'); - // cars +// cars - $plural = str_plural('child'); +$plural = str_plural('child'); - // children +// children +``` #### `str_random()` {#collection-method} The `str_random` function generates a random string of the specified length: - $string = str_random(40); +```php +$string = str_random(40); +``` #### `str_singular()` {#collection-method} The `str_singular` function converts a string to its singular form. This function currently only supports the English language: - $singular = str_singular('cars'); +```php +$singular = str_singular('cars'); - // car +// car +``` #### `str_slug()` {#collection-method} The `str_slug` function generates a URL friendly "slug" from the given string: - $title = str_slug("Winter CMS", "-"); +```php +$title = str_slug("Winter CMS", "-"); - // winter-cms +// winter-cms +``` #### `studly_case()` {#collection-method} The `studly_case` function converts the given string to `StudlyCase`: - $value = studly_case('foo_bar'); +```php +$value = studly_case('foo_bar'); - // FooBar +// FooBar +``` #### `trans()` {#collection-method} The `trans` function translates the given language line using your [localization files](../plugin/localization): - echo trans('validation.required'): +```php +echo trans('validation.required'): +``` #### `trans_choice()` {#collection-method} The `trans_choice` function translates the given language line with inflection: - $value = trans_choice('foo.bar', $count); +```php +$value = trans_choice('foo.bar', $count); +``` ## Miscellaneous @@ -654,135 +769,173 @@ The `trans_choice` function translates the given language line with inflection: Generate a URL for an asset using the current scheme of the request (HTTP or HTTPS): - $url = asset('img/photo.jpg'); +```php +$url = asset('img/photo.jpg'); +``` #### `config()` {#collection-method} The `config` function gets the value of a configuration variable. The configuration values may be accessed using "dot" syntax, which includes the name of the file and the option you wish to access. A default value may be specified and is returned if the configuration option does not exist: - $value = config('app.timezone'); +```php +$value = config('app.timezone'); - $value = config('app.timezone', $default); +$value = config('app.timezone', $default); +``` The `config` helper may also be used to set configuration variables at runtime by passing an array of key / value pairs: - config(['app.debug' => true]); +```php +config(['app.debug' => true]); +``` #### `dd()` {#collection-method} The `dd` function dumps the given variable and ends execution of the script: - dd($value); +```php +dd($value); +``` #### `env()` {#collection-method} The `env` function gets the value of an environment variable or returns a default value: - $env = env('APP_ENV'); +```php +$env = env('APP_ENV'); - // Return a default value if the variable doesn't exist... - $env = env('APP_ENV', 'production'); +// Return a default value if the variable doesn't exist... +$env = env('APP_ENV', 'production'); +``` #### `get()` {#collection-method} The `get` function obtains an input item from the request, restricted to GET variables only: - $value = get('key', $default = null) +```php +$value = get('key', $default = null) +``` #### `input()` {#collection-method} The `input` function obtains an input item from the request: - $value = input('key', $default = null) +```php +$value = input('key', $default = null) +``` #### `post()` {#collection-method} The `post` function obtains an input item from the request, restricted to POST variables only: - $value = post('key', $default = null) +```php +$value = post('key', $default = null) +``` #### `redirect()` {#collection-method} The `redirect` function return an instance of the redirector to do [redirect responses](../services/response-view#redirects): - return redirect('/home'); +```php +return redirect('/home'); +``` #### `request()` {#collection-method} The `request` function returns the current [request instance](../services/request-input): - $referer = request()->header('referer'); +```php +$referer = request()->header('referer'); +``` #### `response()` {#collection-method} The `response` function creates a [response](../services/response-view) instance or obtains an instance of the response factory: - return response('Hello World', 200, $headers); +```php +return response('Hello World', 200, $headers); - return response()->json(['foo' => 'bar'], 200, $headers); +return response()->json(['foo' => 'bar'], 200, $headers); +``` #### `route()` {#collection-method} The `route` function generates a URL for the given [named route](../services/router): - $url = route('routeName'); +```php +$url = route('routeName'); +``` If the route accepts parameters, you may pass them as the second argument to the method: - $url = route('routeName', ['id' => 1]); +```php +$url = route('routeName', ['id' => 1]); +``` #### `secure_asset()` {#collection-method} Generate a URL for an asset using HTTPS: - echo secure_asset('foo/bar.zip', $title, $attributes = []); +```php +echo secure_asset('foo/bar.zip', $title, $attributes = []); +``` #### `trace_log()` {#collection-method} The `trace_log` function writes a trace message to the log file. - trace_log('This code has passed...'); +```php +trace_log('This code has passed...'); +``` The function supports passing exceptions, arrays and objects: - trace_log($exception); +```php +trace_log($exception); - trace_log($array); +trace_log($array); - trace_log($object); +trace_log($object); +``` You may also pass multiple arguments to trace multiple messages: - trace_log($value1, $value2, $exception, '...'); +```php +trace_log($value1, $value2, $exception, '...'); +``` #### `trace_sql()` {#collection-method} The `trace_sql` function enables database logging and begins to monitor all SQL output. - trace_sql(); +```php +trace_sql(); - Db::table('users')->count(); +Db::table('users')->count(); - // select count(*) as aggregate from users +// select count(*) as aggregate from users +``` #### `url()` {#collection-method} The `url` function generates a fully qualified URL to the given path: - echo url('user/profile'); +```php +echo url('user/profile'); - echo url('user/profile', [1]); +echo url('user/profile', [1]); +``` diff --git a/services-html.md b/services-html.md index ddd2864a..688ef6ca 100644 --- a/services-html.md +++ b/services-html.md @@ -18,11 +18,13 @@ Winter provides various helpful functions with the `Html` facade, useful for dealing with HTML and forms. While most of the examples will use the PHP language all of these features translate directly to [Twig markup](../markup) with a simple conversion. - // PHP - +```php +// PHP + - // Twig - {{ form_open(...) }} +// Twig +{{ form_open(...) }} +``` As you can see above, in Twig all functions prefixed with `form_` will bind directly to the `Form` facade and provide access to the methods using *snake_case*. See the [markup guide for more information](../markup/function-form) on using the form helper in the frontend. @@ -31,45 +33,61 @@ As you can see above, in Twig all functions prefixed with `form_` will bind dire Forms can be opened with the `Form::open` method that passes an array of attributes as the first argument: - 'foo/bar']) ?> - // - +```php + 'foo/bar']) ?> + // + +``` By default, a `POST` method will be assumed, however, you are free to specify another method: - Form::open(['url' => 'foo/bar', 'method' => 'put']) +```php +Form::open(['url' => 'foo/bar', 'method' => 'put']) +``` > **NOTE:** Since HTML forms only support `POST` and `GET`, `PUT` and `DELETE` methods will be spoofed by automatically adding a `_method` hidden field to your form. You may pass in regular HTML attributes as well: - Form::open(['url' => 'foo/bar', 'class' => 'pretty-form']) +```php +Form::open(['url' => 'foo/bar', 'class' => 'pretty-form']) +``` If your form is going to accept file uploads, add a `files` option to your array: - Form::open(['url' => 'foo/bar', 'files' => true]) +```php +Form::open(['url' => 'foo/bar', 'files' => true]) +``` You may also open forms that point to handler methods in your page or components: - Form::open(['request' => 'onSave']) +```php +Form::open(['request' => 'onSave']) +``` #### AJAX enabled forms Likewise, AJAX enabled forms can be opened using the `Form::ajax` method where the first argument is the handler method name: - Form::ajax('onSave') +```php +Form::ajax('onSave') +``` The second argument of `Form::ajax` should contain the attributes: - Form::ajax('onSave', ['confirm' => 'Are you sure?']) +```php +Form::ajax('onSave', ['confirm' => 'Are you sure?']) +``` You can also pass partials to update as another array: - Form::ajax('onSave', ['update' => [ - 'control-panel' => '#controlPanel', - 'layout/sidebar' => '#layoutSidebar' - ] - ]) +```php +Form::ajax('onSave', ['update' => [ + 'control-panel' => '#controlPanel', + 'layout/sidebar' => '#layoutSidebar' + ] +]) +``` > **NOTE**: Most [data attributes from the AJAX framework](../ajax/attributes-api) are available here by dropping the `data-request-` prefix. @@ -80,13 +98,17 @@ You can also pass partials to update as another array: If you have [protection enabled](../setup/configuration#csrf-protection), using the `Form::open` method with `POST`, `PUT` or `DELETE` will automatically add a CSRF token to your forms as a hidden field. Alternatively, if you wish to generate the HTML for the hidden CSRF field, you may use the `token` method: - +```php + +``` #### Deferred binding session key A session key used for [deferred binding](../database/relations#deferred-binding) will be added to every form as a hidden field. If you want to generate this field manually, you may use the `sessionKey` method: - +```php + +``` ## Form model binding @@ -95,7 +117,9 @@ A session key used for [deferred binding](../database/relations#deferred-binding You may want to populate a form based on the contents of a model. To do so, use the `Form::model` method: - 'userForm']) ?> +```php + 'userForm']) ?> +``` Now when you generate a form element, like a text input, the model's value matching the field's name will automatically be set as the field value. So for example, for a text input named `email`, the user model's `email` attribute would be set as the value. If there is an item in the Session flash data matching the input name, that will take precedence over the model's value. The priority looks like this: @@ -106,11 +130,15 @@ Now when you generate a form element, like a text input, the model's value match This allows you to quickly build forms that not only bind to model values, but easily re-populate if there is a validation error on the server. You can manually access these values using `Form::value`: - +```php + +``` You may pass a default value as the second argument: - +```php + +``` > **NOTE:** When using `Form::model`, be sure to close your form with `Form::close`! @@ -119,11 +147,15 @@ You may pass a default value as the second argument: #### Generating a label element - +```php + +``` #### Specifying extra HTML attributes - 'awesome']) ?> +```php + 'awesome']) ?> +``` > **NOTE:** After creating a label, any form element you create with a name matching the label name will automatically receive an ID matching the label name as well. @@ -132,51 +164,67 @@ You may pass a default value as the second argument: #### Generating A Text Input - +```php + +``` #### Specifying a default value - +```php + +``` > **NOTE:** The *hidden* and *textarea* methods have the same signature as the *text* method. #### Generating a password input - +```php + +``` #### Generating other inputs - - +```php + + +``` ## Checkboxes and radio buttons #### Generating a checkbox or radio input - +```php + - + +``` #### Generating a checkbox or radio input that is checked - +```php + - + +``` ## Number #### Generating a number input - +```php + +``` ## File input #### Generating a file input - +```php + +``` > **NOTE:** The form must have been opened with the `files` option set to `true`. @@ -185,41 +233,57 @@ You may pass a default value as the second argument: #### Generating a drop-down list - 'Large', 'S' => 'Small']) ?> +```php + 'Large', 'S' => 'Small']) ?> +``` #### Generating a drop-down list with selected default - 'Large', 'S' => 'Small'], 'S') ?> +```php + 'Large', 'S' => 'Small'], 'S') ?> +``` #### Generating a grouped list - ['leopard' => 'Leopard'], - 'Dogs' => ['spaniel' => 'Spaniel'], - ]) ?> +```php + ['leopard' => 'Leopard'], + 'Dogs' => ['spaniel' => 'Spaniel'], +]) ?> +``` #### Generating a drop-down list with a range - +```php + +``` #### Generating a drop-down list with a range, selected value and blank option - 'Choose...']) ?> +```php + 'Choose...']) ?> +``` #### Generating a list with month names - +```php + +``` #### Generating a list with month names, selected value and blank option - 'Choose month...']) ?> +```php + 'Choose month...']) ?> +``` ## Buttons #### Generating a submit button - +```php + +``` > **NOTE:** Need to create a button element? Try the *button* method. It has the same signature as *submit*. @@ -230,12 +294,16 @@ You may pass a default value as the second argument: It's easy to define your own custom Form class helpers called "macros". Here's how it works. First, simply register the macro with a given name and a Closure: - Form::macro('myField', function() { - return ''; - }) +```php +Form::macro('myField', function() { + return ''; +}) +``` Now you can call your macro using its name: #### Calling A Custom Form Macro - +```php + +``` diff --git a/services-image-resizing.md b/services-image-resizing.md index a9996cea..fc6b7755 100644 --- a/services-image-resizing.md +++ b/services-image-resizing.md @@ -47,6 +47,14 @@ If `$width` or `$height` is falsey or `'auto'`, that value is calculated using o The following elements are supported in the options array are supported: + +
    + Key | Description | Default | Options --- | --- | --- | --- `mode` | How the image should be fitted to dimensions | `auto` | `exact`, `portrait`, `landscape`, `auto`, `fit`, or `crop` diff --git a/services-mail.md b/services-mail.md index ee0c59a1..6def2baf 100644 --- a/services-mail.md +++ b/services-mail.md @@ -26,48 +26,58 @@ Before using the Mailgun, SparkPost or SES drivers you will need to install [Dri To use the Mailgun driver, set the `driver` option in your `config/mail.php` configuration file to `mailgun`. Next, verify that your `config/services.php` configuration file contains the following options: - 'mailgun' => [ - 'domain' => 'your-mailgun-domain', - 'secret' => 'your-mailgun-key', - 'endpoint' => 'api.mailgun.net', // api.eu.mailgun.net for EU - ], +```php +'mailgun' => [ + 'domain' => 'your-mailgun-domain', + 'secret' => 'your-mailgun-key', + 'endpoint' => 'api.mailgun.net', // api.eu.mailgun.net for EU +], +``` #### SparkPost driver To use the SparkPost driver set the `driver` option in your `config/mail.php` configuration file to `sparkpost`. Next, verify that your `config/services.php` configuration file contains the following options: - 'sparkpost' => [ - 'secret' => 'your-sparkpost-key', - ], +```php +'sparkpost' => [ + 'secret' => 'your-sparkpost-key', +], +``` #### SES driver To use the Amazon SES driver set the `driver` option in your `config/mail.php` configuration file to `ses`. Then, verify that your `config/services.php` configuration file contains the following options: - 'ses' => [ - 'key' => 'your-ses-key', - 'secret' => 'your-ses-secret', - 'region' => 'ses-region', // e.g. us-east-1 - ], +```php +'ses' => [ + 'key' => 'your-ses-key', + 'secret' => 'your-ses-secret', + 'region' => 'ses-region', // e.g. us-east-1 +], +``` ## Sending mail To send a message, use the `send` method on the `Mail` facade which accepts three arguments. The first argument is a unique *mail code* used to locate either the [mail view](#mail-views) or [mail template](#mail-templates). The second argument is an array of data you wish to pass to the view. The third argument is a `Closure` callback which receives a message instance, allowing you to customize the recipients, subject, and other aspects of the mail message: - // These variables are available inside the message as Twig - $vars = ['name' => 'Joe', 'user' => 'Mary']; +```php +// These variables are available inside the message as Twig +$vars = ['name' => 'Joe', 'user' => 'Mary']; - Mail::send('acme.blog::mail.message', $vars, function($message) { +Mail::send('acme.blog::mail.message', $vars, function($message) { - $message->to('admin@domain.tld', 'Admin Person'); - $message->subject('This is a reminder'); + $message->to('admin@domain.tld', 'Admin Person'); + $message->subject('This is a reminder'); - }); +}); +``` Since we are passing an array containing the `name` key in the example above, we could display the value within our e-mail view using the following Twig markup: - {{ name }} +```twig +{{ name }} +``` > **NOTE:** You should avoid passing a `message` variable in your message, this variable is always passed and allows the [inline embedding of attachments](#attachments). @@ -75,20 +85,28 @@ Since we are passing an array containing the `name` key in the example above, we Winter also includes an alternative method called `sendTo` that can simplify sending mail: - // Send to address using no name - Mail::sendTo('admin@domain.tld', 'acme.blog::mail.message', $params); +```php +// Send to address using no name +Mail::sendTo('admin@domain.tld', 'acme.blog::mail.message', $params); - // Send using an object's properties - Mail::sendTo($user, 'acme.blog::mail.message', $params); +// Send using an object's properties +Mail::sendTo($user, 'acme.blog::mail.message', $params); - // Send to multiple addresses - Mail::sendTo(['admin@domain.tld' => 'Admin Person'], 'acme.blog::mail.message', $params); +// Send to multiple addresses +Mail::sendTo(['admin@domain.tld' => 'Admin Person'], 'acme.blog::mail.message', $params); - // Alternatively send a raw message without parameters - Mail::rawTo('admin@domain.tld', 'Hello friend'); +// Alternatively send a raw message without parameters +Mail::rawTo('admin@domain.tld', 'Hello friend'); +``` The first argument in `sendTo` is used for the recipients can take different value types: + +
    + Type | Description ------------- | ------------- `String` | a single recipient address, with no name defined. @@ -98,7 +116,9 @@ Type | Description The complete signature of `sendTo` is as follows: - Mail::sendTo($recipient, $message, $params, $callback, $options); +```php +Mail::sendTo($recipient, $message, $params, $callback, $options); +``` - `$recipient` is defined as above. - `$message` is the template name or message contents for raw sending. @@ -115,30 +135,34 @@ The following custom sending `$options` are supported As previously mentioned, the third argument given to the `send` method is a `Closure` allowing you to specify various options on the e-mail message itself. Using this Closure you may specify other attributes of the message, such as carbon copies, blind carbon copies, etc: - Mail::send('acme.blog::mail.welcome', $vars, function($message) { +```php +Mail::send('acme.blog::mail.welcome', $vars, function($message) { - $message->from('us@example.com', 'Winter'); - $message->to('foo@example.com')->cc('bar@example.com'); + $message->from('us@example.com', 'Winter'); + $message->to('foo@example.com')->cc('bar@example.com'); - }); +}); +``` Here is a list of the available methods on the `$message` message builder instance: - $message->from($address, $name = null); - $message->sender($address, $name = null); - $message->to($address, $name = null); - $message->cc($address, $name = null); - $message->bcc($address, $name = null); - $message->replyTo($address, $name = null); - $message->subject($subject); - $message->priority($level); - $message->attach($pathToFile, array $options = []); +```php +$message->from($address, $name = null); +$message->sender($address, $name = null); +$message->to($address, $name = null); +$message->cc($address, $name = null); +$message->bcc($address, $name = null); +$message->replyTo($address, $name = null); +$message->subject($subject); +$message->priority($level); +$message->attach($pathToFile, array $options = []); - // Attach a file from a raw $data string... - $message->attachData($data, $name, array $options = []); +// Attach a file from a raw $data string... +$message->attachData($data, $name, array $options = []); - // Get the underlying SwiftMailer message instance... - $message->getSwiftMessage(); +// Get the underlying SwiftMailer message instance... +$message->getSwiftMessage(); +``` > **NOTE:** The message instance passed to a `Mail::send` Closure extends the [SwiftMailer](http://swiftmailer.org) message class, allowing you to call any method on that class to build your e-mail messages. @@ -146,51 +170,65 @@ Here is a list of the available methods on the `$message` message builder instan By default, the view given to the `send` method is assumed to contain HTML. However, by passing an array as the first argument to the `send` method, you may specify a plain text view to send in addition to the HTML view: - Mail::send(['acme.blog::mail.html', 'acme.blog::mail.text'], $data, $callback); +```php +Mail::send(['acme.blog::mail.html', 'acme.blog::mail.text'], $data, $callback); +``` Or, if you only need to send a plain text e-mail, you may specify this using the `text` key in the array: - Mail::send(['text' => 'acme.blog::mail.text'], $data, $callback); +```php +Mail::send(['text' => 'acme.blog::mail.text'], $data, $callback); +``` #### Mailing parsed raw strings You may use the `raw` method if you wish to e-mail a raw string directly. This content will be parsed by Markdown. - Mail::raw('Text to e-mail', function ($message) { - // - }); +```php +Mail::raw('Text to e-mail', function ($message) { + // +}); +``` Additionally this string will be parsed by Twig, if you wish to pass variables to this environment, use the `send` method instead, passing the content as the `raw` key. - Mail::send(['raw' => 'Text to email'], $vars, function ($message) { - // - }); +```php +Mail::send(['raw' => 'Text to email'], $vars, function ($message) { + // +}); +``` #### Mailing raw strings If you pass an array containing either `text` or `html` keys, this will be an explicit request to send mail. No layout or markdown parsing is used. - Mail::raw([ - 'text' => 'This is plain text', - 'html' => 'This is HTML' - ], function ($message) { - // - }); +```php +Mail::raw([ + 'text' => 'This is plain text', + 'html' => 'This is HTML' +], function ($message) { + // +}); +``` ### Attachments To add attachments to an e-mail, use the `attach` method on the `$message` object passed to your Closure. The `attach` method accepts the full path to the file as its first argument: - Mail::send('acme.blog::mail.welcome', $data, function ($message) { - // +```php +Mail::send('acme.blog::mail.welcome', $data, function ($message) { + // - $message->attach($pathToFile); - }); + $message->attach($pathToFile); +}); +``` When attaching files to a message, you may also specify the display name and / or MIME type by passing an `array` as the second argument to the `attach` method: - $message->attach($pathToFile, ['as' => $display, 'mime' => $mime]); +```php +$message->attach($pathToFile, ['as' => $display, 'mime' => $mime]); +``` ### Inline attachments @@ -199,29 +237,35 @@ When attaching files to a message, you may also specify the display name and / o Embedding inline images into your e-mails is typically cumbersome; however, there is a convenient way to attach images to your e-mails and retrieving the appropriate CID. To embed an inline image, use the `embed` method on the `message` variable within your e-mail view. Remember, the `message` variable is available to all of your mail views: - - Here is an image: +```twig + + Here is an image: - - + + +``` If you are planning to use queued emails make sure that the path of the file is absolute. To achieve that you can simply use the [app filter](../markup/filter-app): - - Here is an image: - {% set pathToFile = 'storage/app/media/path/to/file.jpg' | app %} - - +```twig + + Here is an image: + {% set pathToFile = 'storage/app/media/path/to/file.jpg' | app %} + + +``` #### Embedding raw data in mail content If you already have a raw data string you wish to embed into an e-mail message, you may use the `embedData` method on the `message` variable: - - Here is an image from raw data: +```twig + + Here is an image from raw data: - - + + +``` ### Queueing mail @@ -230,9 +274,11 @@ If you already have a raw data string you wish to embed into an e-mail message, Since sending mail messages can drastically lengthen the response time of your application, many developers choose to queue messages for background sending. This is easy using the built-in [unified queue API](../services/queues). To queue a mail message, use the `queue` method on the `Mail` facade: - Mail::queue('acme.blog::mail.welcome', $data, function ($message) { - // - }); +```php +Mail::queue('acme.blog::mail.welcome', $data, function ($message) { + // +}); +``` This method will automatically take care of pushing a job onto the queue to send the mail message in the background. Of course, you will need to [configure your queues](../services/queues) before using this feature. @@ -240,21 +286,25 @@ This method will automatically take care of pushing a job onto the queue to send If you wish to delay the delivery of a queued e-mail message, you may use the `later` method. To get started, simply pass the number of seconds by which you wish to delay the sending of the message as the first argument to the method: - Mail::later(5, 'acme.blog::mail.welcome', $data, function ($message) { - // - }); +```php +Mail::later(5, 'acme.blog::mail.welcome', $data, function ($message) { + // +}); +``` #### Pushing to specific queues If you wish to specify a specific queue on which to push the message, you may do so using the `queueOn` and `laterOn` methods: - Mail::queueOn('queue-name', 'acme.blog::mail.welcome', $data, function ($message) { - // - }); +```php +Mail::queueOn('queue-name', 'acme.blog::mail.welcome', $data, function ($message) { + // +}); - Mail::laterOn('queue-name', 5, 'acme.blog::mail.welcome', $data, function ($message) { - // - }); +Mail::laterOn('queue-name', 5, 'acme.blog::mail.welcome', $data, function ($message) { + // +}); +``` ## Message content @@ -268,43 +318,49 @@ Optionally, mail views can be [registered in the Plugin registration file](#mail Mail views reside in the file system and the code used represents the path to the view file. For example sending mail with the code **author.plugin::mail.message** would use the content in following file: - plugins/ <=== Plugins directory - author/ <=== "author" segment - plugin/ <=== "plugin" segment - views/ <=== View directory - mail/ <=== "mail" segment - message.htm <=== "message" segment +```css +📂 plugins <=== Plugins directory + ┗ 📂 author <=== "author" segment + ┗ 📂 plugin <=== "plugin" segment + ┗ 📂 views <=== View directory + ┗ 📂 mail <=== "mail" segment + ┗ 📜 message.htm <=== "message" segment +``` The content inside a mail view file can include up to 3 sections: **configuration**, **plain text**, and **HTML markup**. Sections are separated with the `==` sequence. For example: - subject = "Your product has been added to Winter CMS project" - == +```twig +subject = "Your product has been added to Winter CMS project" +== - Hi {{ name }}, +Hi {{ name }}, - Good news! User {{ user }} just added your product "{{ product }}" to a project. +Good news! User {{ user }} just added your product "{{ product }}" to a project. - This message was sent using no formatting (plain text) - == +This message was sent using no formatting (plain text) +== -

    Hi {{ name }},

    +

    Hi {{ name }},

    -

    Good news! User {{ user }} just added your product {{ product }} to a project.

    +

    Good news! User {{ user }} just added your product {{ product }} to a project.

    -

    This email was sent using formatting (HTML)

    +

    This email was sent using formatting (HTML)

    +``` > **NOTE:** Basic Twig tags and expressions are supported in mail views. The **plain text** section is optional and a view can contain only the configuration and HTML markup sections. - subject = "Your product has been added to Winter CMS project" - == +```twig +subject = "Your product has been added to Winter CMS project" +== -

    Hi {{ name }},

    +

    Hi {{ name }},

    -

    This email does not support plain text.

    +

    This email does not support plain text.

    -

    Sorry about that!

    +

    Sorry about that!

    +``` #### Configuration section @@ -312,8 +368,8 @@ The configuration section sets the mail view parameters. The following configura Parameter | Description ------------- | ------------- -**subject** | the mail message subject, required. -**layout** | the [mail layout](#mail-layouts) code, optional. Default value is `default`. +`subject` | the mail message subject, required. +`layout` | the [mail layout](#mail-layouts) code, optional. Default value is `default`. ### Using mail templates @@ -322,10 +378,12 @@ Mail templates reside in the database and can be created in the backend area via The process for sending these emails is the same. For example, if you create a template with code *this.is.my.email* you can send it using this PHP code: - Mail::send('this.is.my.email', $data, function($message) use ($user) - { - [...] - }); +```php +Mail::send('this.is.my.email', $data, function($message) use ($user) +{ + [...] +}); +``` > **NOTE:** If the mail template does not exist in the system, this code will attempt to find a mail view with the same code. @@ -344,43 +402,45 @@ By default, Winter comes with two primary mail layouts: Layout | Code | Description ------------- | ------------- | ------------- -Default | default | Used for public facing, frontend mail -System | system | Used for internal, backend mail +Default | `default` | Used for public facing, frontend mail +System | `system` | Used for internal, backend mail ### Registering mail layouts, templates & partials Mail views can be registered as templates that are automatically generated in the backend ready for customization. Mail templates can be customized via the *Settings > Mail templates* menu. The templates can be registered by overriding the `registerMailTemplates` method of the [Plugin registration class](../plugin/registration#registration-file). - public function registerMailTemplates() - { - return [ - 'winter.user::mail.activate', - 'winter.user::mail.restore' - ]; - } +```php +public function registerMailTemplates() +{ + return [ + 'winter.user::mail.activate', + 'winter.user::mail.restore' + ]; +} +``` The method should return an array of [mail view names](#mail-views). Like templates, mail partials and layouts can be registered by overriding the `registerMailPartials` and `registerMailLayouts` methods of the [Plugin registration class](../plugin/registration#registration-file). - - public function registerMailPartials() - { - return [ - 'tracking' => 'acme.blog::partials.tracking', - 'promotion' => 'acme.blog::partials.promotion', - ]; - } - - public function registerMailLayouts() - { - return [ - 'marketing' => 'acme.blog::layouts.marketing', - 'notification' => 'acme.blog::layouts.notification', - ]; - } - +```php +public function registerMailPartials() +{ + return [ + 'tracking' => 'acme.blog::partials.tracking', + 'promotion' => 'acme.blog::partials.promotion', + ]; +} + +public function registerMailLayouts() +{ + return [ + 'marketing' => 'acme.blog::layouts.marketing', + 'notification' => 'acme.blog::layouts.notification', + ]; +} +``` The methods should return an array of [mail view names](#mail-views). The array key will be used as `code` property for the partial or layout. @@ -389,7 +449,9 @@ The methods should return an array of [mail view names](#mail-views). The array You may register variables that are globally available to all mail templates with the `View::share` method. - View::share('site_name', 'Winter CMS'); +```php +View::share('site_name', 'Winter CMS'); +``` This code could be called inside the register or boot method of a [plugin registration file](../plugin/registration). Using the above example, the variable `{{ site_name }}` will be available inside all mail templates. @@ -406,13 +468,17 @@ One solution is to use the `log` mail driver during local development. This driv Another solution is to set a universal recipient of all e-mails sent by the framework. This way, all the emails generated by your application will be sent to a specific address, instead of the address actually specified when sending the message. This can be done via the `to` option in your `config/mail.php` configuration file: - 'to' => [ - 'address' => 'dev@example.com', - 'name' => 'Dev Example' - ], +```php +'to' => [ + 'address' => 'dev@example.com', + 'name' => 'Dev Example' +], +``` #### Pretend mail mode You can dynamically disable sending mail using the `Mail::pretend` method. When the mailer is in pretend mode, messages will be written to your application's log files instead of being sent to the recipient. - Mail::pretend(); +```php +Mail::pretend(); +``` diff --git a/services-pagination.md b/services-pagination.md index f3f918d4..aff84d1a 100644 --- a/services-pagination.md +++ b/services-pagination.md @@ -19,7 +19,9 @@ There are several ways to paginate items. The simplest is by using the `paginate First, let's take a look at calling the `paginate` method on a query. In this example, the only argument passed to `paginate` is the number of items you would like displayed "per page". In this case, let's specify that we would like to display `15` items per page: - $users = Db::table('users')->paginate(15); +```php +$users = Db::table('users')->paginate(15); +``` > **NOTE:** Currently, pagination operations that use a `groupBy` statement cannot be executed efficiently. If you need to use a `groupBy` with a paginated result set, it is recommended that you query the database and create a paginator manually. @@ -27,26 +29,36 @@ First, let's take a look at calling the `paginate` method on a query. In this ex If you only need to display simple "Next" and "Previous" links in your pagination view, you have the option of using the `simplePaginate` method to perform a more efficient query. This is very useful for large datasets if you do not need to display a link for each page number when rendering your view: - $users = Db::table('users')->simplePaginate(15); +```php +$users = Db::table('users')->simplePaginate(15); +``` ### Paginating model results You may also paginate [database model](../database/model) queries. In this example, we will paginate the `User` model with `15` items per page. As you can see, the syntax is nearly identical to paginating query builder results: - $users = User::paginate(15); +```php +$users = User::paginate(15); +``` Of course, you may call `paginate` after setting other constraints on the query, such as `where` clauses: - $users = User::where('votes', '>', 100)->paginate(15); +```php +$users = User::where('votes', '>', 100)->paginate(15); +``` You may also use the `simplePaginate` method when paginating models: - $users = User::where('votes', '>', 100)->simplePaginate(15); +```php +$users = User::where('votes', '>', 100)->simplePaginate(15); +``` You may specify the page number manually by passing a second argument, here we paginate `15` items per page, specifying that we are on page `2`: - $users = User::where('votes', '>', 100)->paginate(15, 2); +```php +$users = User::where('votes', '>', 100)->paginate(15, 2); +``` ### Manually creating a paginator @@ -66,13 +78,15 @@ When you call the `paginate` or `simplePaginate` methods on a query builder or m So once you have retrieved the results, you may display the results and render the page links using Twig: -
    - {% for user in users %} - {{ user.name }} - {% endfor %} -
    +```twig +
    + {% for user in users %} + {{ user.name }} + {% endfor %} +
    - {{ users.render|raw }} +{{ users.render|raw }} +``` The `render` method will render the links to the rest of the pages in the result set. Each of these links will already contain the proper `?page` query string variable. The HTML generated by the `render` method is compatible with the [Bootstrap CSS framework](https://getbootstrap.com). @@ -80,63 +94,75 @@ The `render` method will render the links to the rest of the pages in the result #### Customizing the paginator URI -The `setPath` method allows you to customize the URI used by the paginator when generating links. For example, if you want the paginator to generate links like `http://example.com/custom/url?page=N`, you should pass `custom/url` to the `setPath` method: +The `setPath` method allows you to customize the URI used by the paginator when generating links. For example, if you want the paginator to generate links like `https://example.com/custom/url?page=N`, you should pass `custom/url` to the `setPath` method: - $users = User::paginate(15); - $users->setPath('custom/url'); +```php +$users = User::paginate(15); +$users->setPath('custom/url'); +``` #### Appending to pagination links You may add to the query string of pagination links using the `appends` method. For example, to append `&sort=votes` to each pagination link, you should make the following call to `appends`: - {{ users.appends({sort: 'votes'}).render|raw }} +```twig +{{ users.appends({sort: 'votes'}).render|raw }} +``` If you wish to append a "hash fragment" to the paginator's URLs, you may use the `fragment` method. For example, to append `#foo` to the end of each pagination link, make the following call to the `fragment` method: - {{ users.fragment('foo').render|raw }} +```twig +{{ users.fragment('foo').render|raw }} +``` #### Additional helper methods You may also access additional pagination information via the following methods on paginator instances: - $results->count() - $results->currentPage() - $results->hasMorePages() - $results->lastPage() (Not available when using simplePaginate) - $results->nextPageUrl() - $results->perPage() - $results->previousPageUrl() - $results->total() (Not available when using simplePaginate) - $results->url($page) +```php +$results->count() +$results->currentPage() +$results->hasMorePages() +$results->lastPage() // Not available when using simplePaginate +$results->nextPageUrl() +$results->perPage() +$results->previousPageUrl() +$results->total() // Not available when using simplePaginate +$results->url($page) +``` ## Converting results to JSON The paginator result classes implement the `Illuminate\Contracts\Support\JsonableInterface` contract and expose the `toJson` method, so it's very easy to convert your pagination results to JSON. You may also convert a paginator instance to JSON by simply returning it from a route or AJAX handler: - Route::get('users', function () { - return User::paginate(); - }); +```php +Route::get('users', function () { + return User::paginate(); +}); +``` The JSON from the paginator will include meta information such as `total`, `current_page`, `last_page`, and more. The actual result objects will be available via the `data` key in the JSON array. Here is an example of the JSON created by returning a paginator instance from a route: #### Example Paginator JSON - { - "total": 50, - "per_page": 15, - "current_page": 1, - "last_page": 4, - "next_page_url": "http://wintercms.app?page=2", - "prev_page_url": null, - "from": 1, - "to": 15, - "data":[ - { - // Result Object - }, - { - // Result Object - } - ] - } +```json +{ + "total": 50, + "per_page": 15, + "current_page": 1, + "last_page": 4, + "next_page_url": "https://wintercms.app?page=2", + "prev_page_url": null, + "from": 1, + "to": 15, + "data": [ + { + // Result Object + }, + { + // Result Object + } + ] +} +``` diff --git a/services-parser.md b/services-parser.md index c1354991..5cbcd385 100644 --- a/services-parser.md +++ b/services-parser.md @@ -22,20 +22,26 @@ Winter uses several standards for processing markup, templates and configuration Markdown allows you to write easy-to-read and easy-to-write plain text format, which then converts to HTML. The `Markdown` facade is used for parsing Markdown syntax and is based on [GitHub flavored markdown](https://help.github.com/articles/github-flavored-markdown/). Some quick examples of markdown: - This text is **bold**, this text is *italic*, this text is ~~crossed out~~. +```md +This text is **bold**, this text is *italic*, this text is ~~crossed out~~. - # The largest heading (an

    tag) - ## The second largest heading (an

    tag) - ... - ###### The 6th largest heading (an

    tag) +# The largest heading (an

    tag) +## The second largest heading (an

    tag) +... +###### The 6th largest heading (an

    tag) +``` Use the `Markdown::parse` method to render Markdown to HTML: - $html = Markdown::parse($markdown); +```php +$html = Markdown::parse($markdown); +``` You may also use the `|md` filter for [parsing Markdown in your frontend markup](../markup/filter-md). - {{ '**Text** is bold.'|md }} +```twig +{{ '**Text** is bold.'|md }} +``` ## Twig template parser @@ -44,11 +50,15 @@ Twig is a simple but powerful template engine that parses HTML templates in to o The `Twig` facade is used for parsing Twig syntax, you may use the `Twig::parse` method to render Twig to HTML. - $html = Twig::parse($twig); +```php +$html = Twig::parse($twig); +``` The second argument can be used for passing variables to the Twig markup. - $html = Twig::parse($twig, ['foo' => 'bar']); +```php +$html = Twig::parse($twig, ['foo' => 'bar']); +``` The Twig parser can be extended to register custom features via [the plugin registration file](../plugin/registration#extending-twig). @@ -57,9 +67,11 @@ The Twig parser can be extended to register custom features via [the plugin regi Winter also ships with a simple bracket template parser as an alternative to the Twig parser, currently used for passing variables to [theme content blocks](../cms/content#content-variables). This engine is faster to render HTML and is designed to be more suitable for non-technical users. There is no facade for this parser so the fully qualified `Winter\Storm\Parse\Bracket` class should be used with the `parse` method. - use Winter\Storm\Parse\Bracket; +```php +use Winter\Storm\Parse\Bracket; - $html = Bracket::parse($content, ['foo' => 'bar']); +$html = Bracket::parse($content, ['foo' => 'bar']); +``` The syntax uses singular *curly brackets* for rendering variables: @@ -67,88 +79,112 @@ The syntax uses singular *curly brackets* for rendering variables: You may also pass an array of objects to parse as a variable. - $html = Template::parse($content, ['likes' => [ - ['name' => 'Dogs'], - ['name' => 'Fishing'], - ['name' => 'Golf'] - ]]); +```php +$html = Template::parse($content, ['likes' => [ + ['name' => 'Dogs'], + ['name' => 'Fishing'], + ['name' => 'Golf'] +]]); +``` The array can be iterated using the following syntax: -
      - {likes} -
    • {name}
    • - {/likes} -
    +```twig +
      + {likes} +
    • {name}
    • + {/likes} +
    +``` ## YAML configuration parser YAML ("YAML Ain't Markup Language") is a configuration format, similar to Markdown it was designed to be an easy-to-read and easy-to-write format that converts to a PHP array. It is used practically everywhere for the backend development of Winter, such as [form field](../backend/forms#form-fields) and [list column](../backend/lists##list-columns) definitions. An example of some YAML: - receipt: Acme Purchase Invoice - date: 2015-10-02 - user: - name: Joe - surname: Blogs +```yaml +receipt: Acme Purchase Invoice +date: 2015-10-02 +user: + name: Joe + surname: Blogs +``` The `Yaml` facade is used for parsing YAML and you use the `Yaml::parse` method to render YAML to a PHP array: - $array = Yaml::parse($yamlString); +```php +$array = Yaml::parse($yamlString); +``` Use the `parseFile` method to parse the contents of a file: - $array = Yaml::parseFile($filePath); +```php +$array = Yaml::parseFile($filePath); +``` The parser also supports operation in reverse, outputting YAML format from a PHP array. You may use the `render` method for this: - $yamlString = Yaml::render($array); +```php +$yamlString = Yaml::render($array); +``` ## Initialization (INI) configuration parser The INI file format is a standard for defining simple configuration files, commonly used by [components inside theme templates](../cms/components). It could be considered a cousin of the YAML format, although unlike YAML, it is incredibly simple, less sensitive to typos and does not rely on indentation. It supports basic key-value pairs with sections, for example: - receipt = "Acme Purchase Invoice" - date = "2015-10-02" +```ini +receipt = "Acme Purchase Invoice" +date = "2015-10-02" - [user] - name = "Joe" - surname = "Blogs" +[user] +name = "Joe" +surname = "Blogs" +``` The `Ini` facade is used for parsing INI and you use the `Ini::parse` method to render INI to a PHP array: - $array = Ini::parse($iniString); +```php +$array = Ini::parse($iniString); +``` Use the `parseFile` method to parse the contents of a file: - $array = Ini::parseFile($filePath); +```php +$array = Ini::parseFile($filePath); +``` The parser also supports operation in reverse, outputting INI format from a PHP array. You may use the `render` method for this: - $iniString = Ini::render($array); +```php +$iniString = Ini::render($array); +``` ### Winter flavored INI Traditionally, the INI parser used by the PHP function `parse_ini_string` is restricted to arrays that are 3 levels deep. For example: - level1Value = "foo" - level1Array[] = "bar" +```ini +level1Value = "foo" +level1Array[] = "bar" - [level1Object] - level2Value = "hello" - level2Array[] = "world" - level2Object[level3Value] = "stop here" +[level1Object] +level2Value = "hello" +level2Array[] = "world" +level2Object[level3Value] = "stop here" +``` Winter has extended this functionality with *Winter flavored INI* to allow arrays of infinite depth, inspired by the syntax of HTML forms. Following on from the above example, the following syntax is supported: - [level1Object] - level2Object[level3Array][] = "Yay!" - level2Object[level3Object][level4Value] = "Yay!" - level2Object[level3Object][level4Array][] = "Yay!" - level2Object[level3Object][level4Object][level5Value] = "Yay!" - ; ... to infinity and beyond! +```ini +[level1Object] +level2Object[level3Array][] = "Yay!" +level2Object[level3Object][level4Value] = "Yay!" +level2Object[level3Object][level4Array][] = "Yay!" +level2Object[level3Object][level4Object][level5Value] = "Yay!" +; ... to infinity and beyond! +``` ## Dynamic Syntax parser @@ -159,27 +195,35 @@ Dynamic Syntax is a templating engine unique to Winter that fundamentally suppor There is no facade for this parser so the fully qualified `Winter\Storm\Parse\Syntax\Parser` class should be used with the `parse` method. The first argument of the `parse` method takes the template content as a string and returns a `Parser` object. - use Winter\Storm\Parse\Syntax\Parser as SyntaxParser; +```php +use Winter\Storm\Parse\Syntax\Parser as SyntaxParser; - $syntax = SyntaxParser::parse($content); +$syntax = SyntaxParser::parse($content); +``` ### View mode Let's say we used the first example above as the template content, calling the `render` method by itself will render the template with the default text: - echo $syntax->render(); - //

    Our wonderful website

    +```php +echo $syntax->render(); +//

    Our wonderful website

    +``` Just like any templating engine, passing an array of variables to the first argument of `render` will replace the variables inside the template. Here the default value of `websiteName` is replaced with our new value: - echo $syntax->render(['websiteName' => 'Winter CMS']); - //

    Winter CMS

    +```php +echo $syntax->render(['websiteName' => 'Winter CMS']); +//

    Winter CMS

    +``` As a bonus feature, calling the `toTwig` method will output the template in a prepared state for rendering by the [Twig engine](#twig-parser). - echo $syntax->toTwig(); - //

    {{ websiteName }}

    +```php +echo $syntax->toTwig(); +//

    {{ websiteName }}

    +``` ### Editor mode @@ -188,20 +232,24 @@ So far the Dynamic Syntax parser is not much different to a regular template eng To continue with the examples above, calling the `toEditor` method on the `Parser` object will return a PHP array of properties that define how the variable should be populated, by a form builder for example. - $array = $syntax->toEditor(); - // 'websiteName' => [ - // 'label' => 'Website name', - // 'default' => 'Our wonderful website', - // 'type' => 'text' - // ] +```php +$array = $syntax->toEditor(); +// 'websiteName' => [ +// 'label' => 'Website name', +// 'default' => 'Our wonderful website', +// 'type' => 'text' +// ] +``` You may notice the properties closely resemble the options found in [form field definitions](../backend/forms#form-fields). This is intentional so the two features compliment each other. We could now easily convert the array above to YAML and write to a `fields.yaml` file: - $form = [ - 'fields' => $syntax->toEditor() - ]; +```php +$form = [ + 'fields' => $syntax->toEditor() +]; - File::put('fields.yaml', Yaml::render($form)); +File::put('fields.yaml', Yaml::render($form)); +``` ### Supported tags @@ -276,7 +324,9 @@ Text input for Markdown content. Renders in Twig as - {{ content|md }} +```twig +{{ content|md }} +```
    @@ -288,7 +338,9 @@ File selector for media library items. This tag value will contain the relative Renders in Twig as - {{ logo|media }} +```twig +{{ logo|media }} +```
    @@ -311,31 +363,35 @@ Renders a repeating section with other fields inside. Renders in Twig as - {% for fields in repeater %} -

    {{ fields.title }}

    -

    {{ fields.content|raw }}

    - {% endfor %} +```twig +{% for fields in repeater %} +

    {{ fields.title }}

    +

    {{ fields.content|raw }}

    +{% endfor %} +``` Calling `$syntax->toEditor` will return a different array for a repeater field: - 'repeater' => [ - 'label' => 'Website name', - 'type' => 'repeater', - 'fields' => [ - - 'title' => [ - 'label' => 'Title', - 'default' => 'Title', - 'type' => 'text' - ], - 'content' => [ - 'label' => 'Content', - 'default' => 'Content', - 'type' => 'textarea' - ] - +```php +'repeater' => [ + 'label' => 'Website name', + 'type' => 'repeater', + 'fields' => [ + + 'title' => [ + 'label' => 'Title', + 'default' => 'Title', + 'type' => 'text' + ], + 'content' => [ + 'label' => 'Content', + 'default' => 'Content', + 'type' => 'textarea' ] + ] +] +``` The repeater field also supports group mode, to be used with the dynamic syntax parser as follows: @@ -344,23 +400,25 @@ The repeater field also supports group mode, to be used with the dynamic syntax This is an example of the repeater_fields.yaml group configuration file: - quote: - name: Quote - description: Quote item - icon: icon-quote-right - fields: - quote_position: - span: auto - label: Quote Position - type: radio - options: - left: Left - center: Center - right: Right - quote_content: - span: auto - label: Details - type: textarea +```yaml +quote: + name: Quote + description: Quote item + icon: icon-quote-right + fields: + quote_position: + span: auto + label: Quote Position + type: radio + options: + left: Left + center: Center + right: Right + quote_content: + span: auto + label: Details + type: textarea +``` For more information about the repeater group mode see [Repeater Widget](../backend/forms#widget-repeater). @@ -374,7 +432,9 @@ Text input for rich content (WYSIWYG). Renders in Twig as - {{ content|raw }} +```twig +{{ content|raw }} +```
    diff --git a/services-queues.md b/services-queues.md index de845af6..6d40b388 100644 --- a/services-queues.md +++ b/services-queues.md @@ -175,7 +175,7 @@ Winter includes some [console commands](../console/commands) that will process j To process new jobs as they are pushed onto the queue, run the `queue:work` command: -``` +```bash php artisan queue:work ``` @@ -187,7 +187,7 @@ Queue worker processes store the booted application state in memory. They will n To process only the first job on the queue, use the `--once` option: -``` +```bash php artisan queue:work --once ``` @@ -195,13 +195,13 @@ php artisan queue:work --once You may also specify which queue connection the worker should utilize: -``` +```bash php artisan queue:work --once connection ``` You may pass a comma-delimited list of queue connections to the `work` command to set queue priorities: -``` +```bash php artisan queue:work --once --queue=high,low ``` @@ -211,7 +211,7 @@ In this example, jobs on the `high` queue will always be processed before moving You may also set the length of time (in seconds) each job should be allowed to run: -``` +```bash php artisan queue:work --once --timeout=60 ``` @@ -219,7 +219,7 @@ php artisan queue:work --once --timeout=60 In addition, you may specify the number of seconds to wait before polling for new jobs: -``` +```bash php artisan queue:work --once --sleep=5 ``` @@ -232,7 +232,7 @@ By default `queue:work` will process jobs without ever re-booting the framework. To start a queue worker in daemon mode, simply omit the `--once` flag: -``` +```bash php artisan queue:work connection php artisan queue:work connection --sleep=3 @@ -248,7 +248,7 @@ The simplest way to deploy an application using daemon queue workers is to put t The easiest way to restart your workers is to include the following command in your deployment script: -``` +```bash php artisan queue:restart ``` @@ -278,7 +278,7 @@ Below are two examples of common system daemon managers. You can register a new service to run the queue worker by running the following command as the webhost user in your CLI terminal: -``` +```bash systemctl --user edit --force --full queue-worker.service ``` @@ -303,13 +303,13 @@ You should use your project's root folder as the `WorkingDirectory` definition. Once you save your configuration, you will need to enable it. -``` +```bash systemctl --user enable queue-worker.service ``` If you make any changes to your service's configuration, you must reload the configuration in `systemd`, which can be done by running the following: -``` +```bash systemctl --user daemon-reload ``` @@ -317,19 +317,19 @@ systemctl --user daemon-reload To start your queue worker daemon, simply run the following: -``` +```bash systemctl --user start queue-worker.service ``` And to stop the queue worker daemon: -``` +```bash systemctl --user stop queue-worker.service ``` Finally, to restart it: -``` +```bash systemctl --user restart queue-worker.service ``` @@ -339,7 +339,7 @@ systemctl --user restart queue-worker.service If you wish to check on the status of your queue worker, you can run the following: -``` +```bash systemctl --user status queue-worker.service ``` @@ -347,7 +347,7 @@ By default, `systemd` will show whether your service is active or not, and provi You can also get the full logs by querying `journalctl`: -``` +```bash journalctl --user -u queue-worker.service ``` @@ -356,7 +356,7 @@ journalctl --user -u queue-worker.service Supervisor is a process monitor for the Linux operating system, and will automatically restart your `queue:work` process if it fails. To install Supervisor on Ubuntu, you may use the following command: -``` +```bash sudo apt-get install supervisor ``` @@ -382,7 +382,7 @@ In this example, the `numprocs` directive will instruct Supervisor to run 8 `que Once the configuration file has been created, you may update the Supervisor configuration and start the processes using the following commands: -``` +```bash sudo supervisorctl reread sudo supervisorctl update @@ -399,7 +399,7 @@ Since things don't always go as planned, sometimes your queued jobs will fail. D You can specify the maximum number of times a job should be attempted using the `--tries` switch on the `queue:work` command: -``` +```bash php artisan queue:work connection-name --tries=3 ``` @@ -426,24 +426,24 @@ The original array of `data` will also be automatically passed onto the failed m To view all of your failed jobs, you may use the `queue:failed` Artisan command: -``` +```bash php artisan queue:failed ``` The `queue:failed` command will list the job ID, connection, queue, and failure time. The job ID may be used to retry the failed job. For instance, to retry a failed job that has an ID of 5, the following command should be issued: -``` +```bash php artisan queue:retry 5 ``` If you would like to delete a failed job, you may use the `queue:forget` command: -``` +```bash php artisan queue:forget 5 ``` To delete all of your failed jobs, you may use the `queue:flush` command: -``` +```bash php artisan queue:flush ``` diff --git a/services-request-input.md b/services-request-input.md index e82129f5..51ac4a24 100644 --- a/services-request-input.md +++ b/services-request-input.md @@ -13,31 +13,43 @@ You may access all user input with a few simple methods. You do not need to worr #### Retrieving an input value - $name = Input::get('name'); +```php +$name = Input::get('name'); +``` #### Retrieving a default value if the input value is absent - $name = Input::get('name', 'Sally'); +```php +$name = Input::get('name', 'Sally'); +``` #### Determining if an input value is present - if (Input::has('name')) { - // - } +```php +if (Input::has('name')) { + // +} +``` #### Getting all input for the request - $input = Input::all(); +```php +$input = Input::all(); +``` #### Getting only some of the request input - $input = Input::only('username', 'password'); +```php +$input = Input::only('username', 'password'); - $input = Input::except('credit_card'); +$input = Input::except('credit_card'); +``` When working on forms with "array" inputs, you may use dot notation to access the arrays: - $input = Input::get('products.0.name'); +```php +$input = Input::get('products.0.name'); +``` > **NOTE:** Some JavaScript libraries such as Backbone may send input to the application as JSON. You may access this data via `Input::get` like normal. @@ -50,23 +62,31 @@ By default, all cookies created by the Winter are encrypted and signed with an a #### Retrieving a cookie value - $value = Cookie::get('name'); +```php +$value = Cookie::get('name'); +``` #### Attaching a new cookie to a response - $response = Response::make('Hello World'); +```php +$response = Response::make('Hello World'); - $response->withCookie(Cookie::make('name', 'value', $minutes)); +$response->withCookie(Cookie::make('name', 'value', $minutes)); +``` #### Queueing a cookie for the next response If you would like to set a cookie before a response has been created, use the `Cookie::queue` method. The cookie will automatically be attached to the final response from your application. - Cookie::queue($name, $value, $minutes); +```php +Cookie::queue($name, $value, $minutes); +``` #### Creating a cookie that lasts forever - $cookie = Cookie::forever('name', 'value'); +```php +$cookie = Cookie::forever('name', 'value'); +``` #### Handling cookies without encryption @@ -75,28 +95,32 @@ This is useful, for example, when you want to pass data from frontend to server Add names of the cookies that should not be encrypted or decrypted to `unencryptedCookies` parameter in the `config/cookie.php` configuration file. - /* - |-------------------------------------------------------------------------- - | Cookies without encryption - |-------------------------------------------------------------------------- - | - | Winter CMS encrypts/decrypts cookies by default. You can specify cookies - | that should not be encrypted or decrypted here. This is useful, for - | example, when you want to pass data from frontend to server side backend - | via cookies, and vice versa. - | - */ - - 'unencryptedCookies' => [ - 'my_cookie', - ], +```php +/* +|-------------------------------------------------------------------------- +| Cookies without encryption +|-------------------------------------------------------------------------- +| +| Winter CMS encrypts/decrypts cookies by default. You can specify cookies +| that should not be encrypted or decrypted here. This is useful, for +| example, when you want to pass data from frontend to server side backend +| via cookies, and vice versa. +| +*/ + +'unencryptedCookies' => [ + 'my_cookie', +], +``` Alternatively for plugins, you can also add these dynamically from `Plugin.php` of your plugin. - public function boot() - { - Config::push('cookie.unencryptedCookies', "my_cookie"); - } +```php +public function boot() +{ + Config::push('cookie.unencryptedCookies', "my_cookie"); +} +``` ## Old input @@ -105,72 +129,98 @@ You may need to keep input from one request until the next request. For example, #### Flashing input to the session - Input::flash(); +```php +Input::flash(); +``` #### Flashing only some input to the session - Input::flashOnly('username', 'email'); +```php +Input::flashOnly('username', 'email'); - Input::flashExcept('password'); +Input::flashExcept('password'); +``` Since you often will want to flash input in association with a redirect to the previous page, you may easily chain input flashing onto a redirect. - return Redirect::to('form')->withInput(); +```php +return Redirect::to('form')->withInput(); - return Redirect::to('form')->withInput(Input::except('password')); +return Redirect::to('form')->withInput(Input::except('password')); +``` > **NOTE:** You may flash other data across requests using the [Session](../services/session) class. #### Retrieving old data - Input::old('username'); +```php +Input::old('username'); +``` ## Files #### Retrieving an uploaded file - $file = Input::file('photo'); +```php +$file = Input::file('photo'); +``` #### Determining if a file was uploaded - if (Input::hasFile('photo')) { - // - } +```php +if (Input::hasFile('photo')) { + // +} +``` The object returned by the `file` method is an instance of the `Symfony\Component\HttpFoundation\File\UploadedFile` class, which extends the PHP `SplFileInfo` class and provides a variety of methods for interacting with the file. #### Determining if an uploaded file is valid - if (Input::file('photo')->isValid()) { - // - } +```php +if (Input::file('photo')->isValid()) { + // +} +``` #### Moving an uploaded file - Input::file('photo')->move($destinationPath); +```php +Input::file('photo')->move($destinationPath); - Input::file('photo')->move($destinationPath, $fileName); +Input::file('photo')->move($destinationPath, $fileName); +``` #### Retrieving the path to an uploaded file - $path = Input::file('photo')->getRealPath(); +```php +$path = Input::file('photo')->getRealPath(); +``` #### Retrieving the original name of an uploaded file - $name = Input::file('photo')->getClientOriginalName(); +```php +$name = Input::file('photo')->getClientOriginalName(); +``` #### Retrieving the extension of an uploaded file - $extension = Input::file('photo')->getClientOriginalExtension(); +```php +$extension = Input::file('photo')->getClientOriginalExtension(); +``` #### Retrieving the size of an uploaded file - $size = Input::file('photo')->getSize(); +```php +$size = Input::file('photo')->getSize(); +``` #### Retrieving the MIME type of an uploaded file - $mime = Input::file('photo')->getMimeType(); +```php +$mime = Input::file('photo')->getMimeType(); +``` ## Request information @@ -179,66 +229,90 @@ The `Request` class provides many methods for examining the HTTP request for you #### Retrieving the request URI - $uri = Request::path(); +```php +$uri = Request::path(); +``` #### Retrieving the request method - $method = Request::method(); +```php +$method = Request::method(); - if (Request::isMethod('post')) { - // - } +if (Request::isMethod('post')) { + // +} +``` #### Determining if the request path matches a pattern - if (Request::is('admin/*')) { - // - } +```php +if (Request::is('admin/*')) { + // +} +``` #### Get the request URL - $url = Request::url(); +```php +$url = Request::url(); +``` #### Retrieve a request URI segment - $segment = Request::segment(1); +```php +$segment = Request::segment(1); +``` #### Retrieving a request header - $value = Request::header('Content-Type'); +```php +$value = Request::header('Content-Type'); +``` #### Retrieving values from $_SERVER - $value = Request::server('PATH_INFO'); +```php +$value = Request::server('PATH_INFO'); +``` #### Determining if the request is over HTTPS - if (Request::secure()) { - // - } +```php +if (Request::secure()) { + // +} +``` #### Determine if the request is using AJAX - if (Request::ajax()) { - // - } +```php +if (Request::ajax()) { + // +} +``` #### Determine if the request has JSON content type - if (Request::isJson()) { - // - } +```php +if (Request::isJson()) { + // +} +``` #### Determine if the request is asking for JSON - if (Request::wantsJson()) { - // - } +```php +if (Request::wantsJson()) { + // +} +``` #### Checking the requested response format The `Request::format` method will return the requested response format based on the HTTP Accept header: - if (Request::format() == 'json') { - // - } +```php +if (Request::format() == 'json') { + // +} +``` diff --git a/services-response-view.md b/services-response-view.md index c930f2db..4a7338f8 100644 --- a/services-response-view.md +++ b/services-response-view.md @@ -23,61 +23,77 @@ A response can be returned from almost PHP method that is used by the page. This Returning a string from a CMS page, layout or component method will halt the process at this point and override the default behavior, so here it will display the "Hello World" string instead of displaying the page. - public function onStart() - { - return 'Hello World'; - } +```php +public function onStart() +{ + return 'Hello World'; +} +``` #### Returning strings from AJAX handlers Returning a string from an AJAX handler will add the string to the response collection using the default key of `result`. Requested partials will still be included in the response. - public function onDoSomething() - { - return 'Hello World'; - // ['result' => 'Hello World'] - } +```php +public function onDoSomething() +{ + return 'Hello World'; + // ['result' => 'Hello World'] +} +``` #### Returning strings from routes Returning a string from a [route definition](../services/router) will act the same as a CMS method and display the string as the response. - Route::get('/', function() { - return 'Hello World'; - }); +```php +Route::get('/', function() { + return 'Hello World'; +}); +``` #### Creating custom responses For a more robust solution, returning a `Response` object providing a variety of methods for building HTTP responses. We will explore this topic further in this article. - $contents = 'Page not found'; - $statusCode = 404; - return Response::make($contents, $statusCode); +```php +$contents = 'Page not found'; +$statusCode = 404; +return Response::make($contents, $statusCode); +``` ### Attaching headers to responses Keep in mind that most response methods are chainable, allowing for the fluent building of responses. For example, you may use the `header` method to add a series of headers to the response before sending it back to the user: - return Response::make($content) - ->header('Content-Type', $type) - ->header('X-Header-One', 'Header Value') - ->header('X-Header-Two', 'Header Value'); +```php +return Response::make($content) + ->header('Content-Type', $type) + ->header('X-Header-One', 'Header Value') + ->header('X-Header-Two', 'Header Value'); +``` A practical example of this could be returning an XML response: - return Response::make($xmlString)->header('Content-Type', 'text/xml'); +```php +return Response::make($xmlString)->header('Content-Type', 'text/xml'); +``` ### Attaching cookies to responses The `withCookie` method allows you to easily attach cookies to the response. For example, you may use the withCookie method to generate a cookie and attach it to the response instance: - return Response::make($content)->withCookie('name', 'value'); +```php +return Response::make($content)->withCookie('name', 'value'); +``` The `withCookie` method accepts additional optional arguments which allow you to further customize your cookie's properties: - ->withCookie($name, $value, $minutes, $path, $domain, $secure, $httpOnly) +```php +->withCookie($name, $value, $minutes, $path, $domain, $secure, $httpOnly) +``` ## Other response types @@ -89,30 +105,38 @@ The `Response` facade may be used to conveniently generate other types of respon If you need access to the `Response` class methods, but want to return a [view](#views) as the response content, you may use the `Response::view` method for convenience: - return Response::view('acme.blog::hello')->header('Content-Type', $type); +```php +return Response::view('acme.blog::hello')->header('Content-Type', $type); +``` ### JSON responses The `json` method will automatically set the `Content-Type` header to application/json, as well as convert the given array into JSON using the `json_encode` PHP function: - return Response::json(['name' => 'Steve', 'state' => 'CA']); +```php +return Response::json(['name' => 'Steve', 'state' => 'CA']); +``` If you would like to create a JSONP response, you may use the `json` method in addition to `setCallback`: - return Response::json(['name' => 'Steve', 'state' => 'CA']) - ->setCallback(Input::get('callback')); +```php +return Response::json(['name' => 'Steve', 'state' => 'CA']) + ->setCallback(Input::get('callback')); +``` ### File downloads The `download` method may be used to generate a response that forces the user's browser to download the file at the given path. The `download` method accepts a file name as the second argument to the method, which will determine the file name that is seen by the user downloading the file. Finally, you may pass an array of HTTP headers as the third argument to the method: - return Response::download($pathToFile); +```php +return Response::download($pathToFile); - return Response::download($pathToFile, $name, $headers); +return Response::download($pathToFile, $name, $headers); - return Response::download($pathToFile)->deleteFileAfterSend(true); +return Response::download($pathToFile)->deleteFileAfterSend(true); +``` > **NOTE:** Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII file name. @@ -121,14 +145,18 @@ The `download` method may be used to generate a response that forces the user's Redirect responses are typically instances of the `Illuminate\Http\RedirectResponse` class, and contain the proper headers needed to redirect the user to another URL. The simplest way to generate a `RedirectResponse` instance is to use the `to` method on the `Redirect` facade. - return Redirect::to('user/login'); +```php +return Redirect::to('user/login'); +``` ### Returning a redirect with flash data Redirecting to a new URL and [flashing data to the session](../services/session) are typically done at the same time. So, for convenience, you may create a `RedirectResponse` instance and flash data to the session in a single method chain: - return Redirect::to('user/login')->with('message', 'Login Failed'); +```php +return Redirect::to('user/login')->with('message', 'Login Failed'); +``` > **NOTE:** Since the `with` method flashes data to the session, you may retrieve the data using the typical `Session::get` method. @@ -137,29 +165,37 @@ Redirecting to a new URL and [flashing data to the session](../services/session) You may wish to redirect the user to their previous location, for example, after a form submission. You can do so by using the `back` method: - return Redirect::back(); +```php +return Redirect::back(); - return Redirect::back()->withInput(); +return Redirect::back()->withInput(); +``` #### Redirecting to the current page Sometimes you want to simply refresh the current page, you can do this using the `refresh` method: - return Redirect::refresh(); +```php +return Redirect::refresh(); +``` ## Response macros If you would like to define a custom response that you can re-use in a variety of your routes and controllers, you may use the `Response::macro` method: - Response::macro('caps', function($value) { - return Response::make(strtoupper($value)); - }); +```php +Response::macro('caps', function($value) { + return Response::make(strtoupper($value)); +}); +``` The `macro` function accepts a name as its first argument, and a Closure as its second. The macro's Closure will be executed when calling the macro name on the `Response` class: - return Response::caps('foo'); +```php +return Response::caps('foo'); +``` You may define your macros in the `boot` method of a [Plugin registration file](../plugin/registration#registration-methods). Alternatively, plugins can supply a file named **init.php** in the plugin directory that you can use to place macro registrations. @@ -170,27 +206,33 @@ Views are a great way to store system based presentation logic, such as markup u A simple view could look something like this: - +```twig + - - -

    Hello, {{ name }}

    - - + + +

    Hello, {{ name }}

    + + +``` Views can also be parsed using PHP templating by using the `.php` extension: - +```twig + - - -

    Hello,

    - - + + +

    Hello,

    + + +``` This view may be returned to the browser using the `View::make` method: - return View::make('acme.blog::greeting', ['name' => 'Charlie']); +```php +return View::make('acme.blog::greeting', ['name' => 'Charlie']); +``` The first argument is a "path hint" that contains the plugin name, separated by two colons `::`, followed by the view file name. The second argument passed to `View::make` is an array of data that should be made available to the view. @@ -198,41 +240,53 @@ The first argument is a "path hint" that contains the plugin name, separated by #### Passing data to views - // Using conventional approach - $view = View::make('acme.blog::greeting')->with('name', 'Steve'); +```php +// Using conventional approach +$view = View::make('acme.blog::greeting')->with('name', 'Steve'); - // Using magic methods - $view = View::make('acme.blog::greeting')->withName('steve'); +// Using magic methods +$view = View::make('acme.blog::greeting')->withName('steve'); +``` In the example above the variable `name` would be accessible from the view, and would contain `Steve`. As above, if you want to pass an array of data, you may do so as the second argument given to the `make` method: - $view = View::make('acme.blog::greeting', $data); +```php +$view = View::make('acme.blog::greeting', $data); +``` It is also possible to share a piece of data across all views: - View::share('name', 'Steve'); +```php +View::share('name', 'Steve'); +``` #### Passing a sub-view to a view Sometimes you may wish to pass a view into another view. For example, given a sub-view stored at `plugins/acme/blog/views/child/view.php`, we could pass it to another view like so: - $view = View::make('acme.blog::greeting')->nest('child', 'acme.blog::child.view'); +```php +$view = View::make('acme.blog::greeting')->nest('child', 'acme.blog::child.view'); - $view = View::make('acme.blog::greeting')->nest('child', 'acme.blog::child.view', $data); +$view = View::make('acme.blog::greeting')->nest('child', 'acme.blog::child.view', $data); +``` The sub-view can then be rendered from the parent view: - - -

    Hello!

    - {{ child|raw }} - - +```twig + + +

    Hello!

    + {{ child|raw }} + + +``` #### Determining if a view exists If you need to check if a view exists, use the `View::exists` method: - if (View::exists('acme.blog::mail.customer')) { - // - } +```php +if (View::exists('acme.blog::mail.customer')) { + // +} +``` diff --git a/services-router.md b/services-router.md index 2c892764..ac7779f9 100644 --- a/services-router.md +++ b/services-router.md @@ -19,41 +19,49 @@ While routing is handled automatically for the [backend controllers](../backend/ You can define these routes by creating a file named **routes.php** in a same directory as the [plugin registration file](../plugin/registration). The most basic routes simply accept a URI and a `Closure`: - Route::get('/', function () { - return 'Hello World'; - }); +```php +Route::get('/', function () { + return 'Hello World'; +}); - Route::post('foo/bar', function () { - return 'Hello World'; - }); +Route::post('foo/bar', function () { + return 'Hello World'; +}); - Route::put('foo/bar', function () { - // - }); +Route::put('foo/bar', function () { + // +}); - Route::delete('foo/bar', function () { - // - }); +Route::delete('foo/bar', function () { + // +}); +``` #### Registering a route for multiple verbs Sometimes you may need to register a route that responds to multiple HTTP verbs. You may do so using the `match` method on the `Route` facade: - Route::match(['get', 'post'], '/', function () { - return 'Hello World'; - }); +```php +Route::match(['get', 'post'], '/', function () { + return 'Hello World'; +}); +``` You may even register a route that responds to all HTTP verbs using the `any` method: - Route::any('foo', function () { - return 'Hello World'; - }); +```php +Route::any('foo', function () { + return 'Hello World'; +}); +``` #### Generating URLs to routes You may generate URLs to your routes using the `Url` facade: - $url = Url::to('foo'); +```php +$url = Url::to('foo'); +``` ## Route parameters @@ -63,15 +71,19 @@ You may generate URLs to your routes using the `Url` facade: Sometimes you will need to capture segments of the URI within your route, for example, you may need to capture a user's ID from the URL. You may do so by defining route parameters: - Route::get('user/{id}', function ($id) { - return 'User '.$id; - }); +```php +Route::get('user/{id}', function ($id) { + return 'User '.$id; +}); +``` You may define as many route parameters as required by your route: - Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) { - // - }); +```php +Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) { + // +}); +``` Route parameters are always encased within singular *curly brackets*. The parameters will be passed into your route's `Closure` when the route is executed. @@ -82,65 +94,77 @@ Route parameters are always encased within singular *curly brackets*. The parame Occasionally you may need to specify a route parameter, but make the presence of that route parameter optional. You may do so by placing a `?` mark after the parameter name: - Route::get('user/{name?}', function ($name = null) { - return $name; - }); +```php +Route::get('user/{name?}', function ($name = null) { + return $name; +}); - Route::get('user/{name?}', function ($name = 'John') { - return $name; - }); +Route::get('user/{name?}', function ($name = 'John') { + return $name; +}); +``` ### Regular expression constraints You may constrain the format of your route parameters using the `where` method on a route instance. The `where` method accepts the name of the parameter and a regular expression defining how the parameter should be constrained: - Route::get('user/{name}', function ($name) { - // - })->where('name', '[A-Za-z]+'); +```php +Route::get('user/{name}', function ($name) { + // +})->where('name', '[A-Za-z]+'); - Route::get('user/{id}', function ($id) { - // - })->where('id', '[0-9]+'); +Route::get('user/{id}', function ($id) { + // +})->where('id', '[0-9]+'); - Route::get('user/{id}/{name}', function ($id, $name) { - // - })->where(['id' => '[0-9]+', 'name' => '[a-z]+']); +Route::get('user/{id}/{name}', function ($id, $name) { + // +})->where(['id' => '[0-9]+', 'name' => '[a-z]+']); +``` ## Named routes Named routes allow you to conveniently generate URLs or redirects for a specific route. You may specify a name for a route using the `as` array key when defining the route: - Route::get('user/profile', ['as' => 'profile', function () { - // - }]); +```php +Route::get('user/profile', ['as' => 'profile', function () { + // +}]); +``` #### Route groups & named routes If you are using [route groups](#route-groups), you may specify an `as` keyword in the route group attribute array, allowing you to set a common route name prefix for all routes within the group: - Route::group(['as' => 'admin::'], function () { - Route::get('dashboard', ['as' => 'dashboard', function () { - // Route named "admin::dashboard" - }]); - }); +```php +Route::group(['as' => 'admin::'], function () { + Route::get('dashboard', ['as' => 'dashboard', function () { + // Route named "admin::dashboard" + }]); +}); +``` #### Generating URLs to named routes Once you have assigned a name to a given route, you may use the route's name when generating URLs or redirects via the `Url::route` method: - $url = Url::route('profile'); +```php +$url = Url::route('profile'); - $redirect = Response::redirect()->route('profile'); +$redirect = Response::redirect()->route('profile'); +``` If the route defines parameters, you may pass the parameters as the second argument to the `route` method. The given parameters will automatically be inserted into the URL: - Route::get('user/{id}/profile', ['as' => 'profile', function ($id) { - // - }]); +```php +Route::get('user/{id}/profile', ['as' => 'profile', function ($id) { + // +}]); - $url = Url::route('profile', ['id' => 1]); +$url = Url::route('profile', ['id' => 1]); +``` ## Route groups @@ -152,30 +176,36 @@ Route groups allow you to share route attributes across a large number of routes Route groups may also be used to route wildcard sub-domains. Sub-domains may be assigned route parameters just like route URIs, allowing you to capture a portion of the sub-domain for usage in your route or controller. The sub-domain may be specified using the `domain` key on the group attribute array: - Route::group(['domain' => '{account}.example.com'], function () { - Route::get('user/{id}', function ($account, $id) { - // - }); +```php +Route::group(['domain' => '{account}.example.com'], function () { + Route::get('user/{id}', function ($account, $id) { + // }); +}); +``` ### Route prefixes The `prefix` group array attribute may be used to prefix each route in the group with a given URI. For example, you may want to prefix all route URIs within the group with `admin`: - Route::group(['prefix' => 'admin'], function () { - Route::get('users', function () { - // Matches The "/admin/users" URL - }); +```php +Route::group(['prefix' => 'admin'], function () { + Route::get('users', function () { + // Matches The "/admin/users" URL }); +}); +``` You may also use the `prefix` parameter to specify common parameters for your grouped routes: - Route::group(['prefix' => 'accounts/{account_id}'], function () { - Route::get('detail', function ($account_id) { - // Matches The accounts/{account_id}/detail URL - }); +```php +Route::group(['prefix' => 'accounts/{account_id}'], function () { + Route::get('detail', function ($account_id) { + // Matches The accounts/{account_id}/detail URL }); +}); +``` ### Route Middleware @@ -183,19 +213,25 @@ You may also use the `prefix` parameter to specify common parameters for your gr Registering middleware inside your plugin's `boot()` method will register it globally for each request. If you want to register middleware to one route at a time you should do it like this: - Route::get('info', 'Acme\News@info')->middleware('Path\To\Your\Middleware'); +```php +Route::get('info', 'Acme\News@info')->middleware('Path\To\Your\Middleware'); +``` For route groups it could be done like this: - Route::group(['middleware' => 'Path\To\Your\Middleware'], function() { - Route::get('info', 'Acme\News@info'); - }); +```php +Route::group(['middleware' => 'Path\To\Your\Middleware'], function() { + Route::get('info', 'Acme\News@info'); +}); +``` And finally, if you want to assign a group of middleware to just one route you can it like this - Route::middleware(['Path\To\Your\Middleware'])->group(function() { - Route::get('info', 'Acme\News@info'); - }); +```php +Route::middleware(['Path\To\Your\Middleware'])->group(function() { + Route::get('info', 'Acme\News@info'); +}); +``` You can of course add more than one middleware in a group, just one is used in the above examples for convenience. @@ -204,7 +240,9 @@ You can of course add more than one middleware in a group, just one is used in t There are two ways to manually trigger a 404 error from a route. First, you may use the `abort` helper. The `abort` helper simply throws a `Symfony\Component\HttpFoundation\Exception\HttpException` with the specified status code: - App::abort(404); +```php +App::abort(404); +``` Secondly, you may manually throw an instance of `Symfony\Component\HttpKernel\Exception\NotFoundHttpException`. diff --git a/services-session.md b/services-session.md index 11a1cd99..50c300ab 100644 --- a/services-session.md +++ b/services-session.md @@ -7,7 +7,7 @@ ## Configuration -Since HTTP driven applications are stateless, sessions provide a way to store information about the user across requests. Winter ships with a variety of session backends available for use through a clean, unified API. Support for popular backends such as [Memcached](http://memcached.org), [Redis](http://redis.io), and databases is included out of the box. +Since HTTP driven applications are stateless, sessions provide a way to store information about the user across requests. Winter ships with a variety of session backends available for use through a clean, unified API. Support for popular backends such as [Memcached](https://memcached.org), [Redis](https://redis.io), and databases is included out of the box. The session configuration is stored in `config/session.php`. Be sure to review the well documented options available to you in this file. By default, Winter is configured to use the `file` session driver, which will work well for the majority of applications. @@ -32,67 +32,87 @@ Winter uses the `flash` session key internally, so you should not add an item to Using the `Session` facade you may call a variety of functions to interact with the underlying data. For example, the `put` method stores a new piece of data in the session: - Session::put('key', 'value'); +```php +Session::put('key', 'value'); +``` #### Pushing to array session values The `push` method may be used to push a new value onto a session value that is an array. For example, if the `user.teams` key contains an array of team names, you may push a new value onto the array like so: - Session::push('user.teams', 'developers'); +```php +Session::push('user.teams', 'developers'); +``` #### Retrieving data from the session When you retrieve a value from the session, you may also pass a default value as the second argument to the `get` method. This default value will be returned if the specified key does not exist in the session. If you pass a `Closure` as the default value to the `get` method, the `Closure` will be executed and its result returned: - $value = Session::get('key'); +```php +$value = Session::get('key'); - $value = Session::get('key', 'default'); +$value = Session::get('key', 'default'); - $value = Session::get('key', function() { return 'default'; }); +$value = Session::get('key', function() { return 'default'; }); +``` #### Retrieving all data from the session If you would like to retrieve all data from the session, you may use the `all` method: - $data = Session::all(); +```php +$data = Session::all(); +``` #### Retrieving data and forgetting it The `pull` method will retrieve and delete an item from the session: - $value = Session::pull('key', 'default'); +```php +$value = Session::pull('key', 'default'); +``` #### Determining if an item exists in the session The `has` method may be used to check if an item exists in the session. This method will return `true` if the item exists: - if (Session::has('users')) { - // - } +```php +if (Session::has('users')) { + // +} +``` #### Deleting data from the session The `forget` method will remove a piece of data from the session. If you would like to remove all data from the session, you may use the `flush` method: - Session::forget('key'); +```php +Session::forget('key'); - Session::flush(); +Session::flush(); +``` #### Regenerating the session ID If you need to regenerate the session ID, you may use the `regenerate` method: - Session::regenerate(); +```php +Session::regenerate(); +``` ## Flash data Sometimes you may wish to store items in the session only for the next request. You may do so using the `Session::flash` method. Data stored in the session using this method will only be available during the subsequent HTTP request, and then will be deleted. Flash data is primarily useful for short-lived status messages: - Session::flash('key', 'value'); +```php +Session::flash('key', 'value'); +``` If you need to keep your flash data around for even more requests, you may use the `reflash` method, which will keep all of the flash data around for an additional request. If you only need to keep specific flash data around, you may use the `keep` method: - Session::reflash(); +```php +Session::reflash(); - Session::keep(['username', 'email']); +Session::keep(['username', 'email']); +``` diff --git a/services-validation.md b/services-validation.md index d78a358d..cdf6b166 100644 --- a/services-validation.md +++ b/services-validation.md @@ -22,10 +22,12 @@ The validator class is a simple, convenient facility for validating data and ret #### Basic Validation Example - $validator = Validator::make( - ['name' => 'Joe'], - ['name' => 'required|min:5'] - ); +```php +$validator = Validator::make( + ['name' => 'Joe'], + ['name' => 'required|min:5'] +); +``` The first argument passed to the `make` method is the data under validation. The second argument is the validation rules that should be applied to the data. @@ -33,39 +35,49 @@ The first argument passed to the `make` method is the data under validation. The Multiple rules may be delimited using either a "pipe" character, or as separate elements of an array. - $validator = Validator::make( - ['name' => 'Joe'], - ['name' => ['required', 'min:5']] - ); +```php +$validator = Validator::make( + ['name' => 'Joe'], + ['name' => ['required', 'min:5']] +); +``` #### Validating multiple fields - $validator = Validator::make( - [ - 'name' => 'Joe', - 'password' => 'lamepassword', - 'email' => 'email@example.com' - ], - [ - 'name' => 'required', - 'password' => 'required|min:8', - 'email' => 'required|email|unique:users' - ] - ); +```php +$validator = Validator::make( + [ + 'name' => 'Joe', + 'password' => 'lamepassword', + 'email' => 'email@example.com' + ], + [ + 'name' => 'required', + 'password' => 'required|min:8', + 'email' => 'required|email|unique:users' + ] +); +``` Once a `Validator` instance has been created, the `fails` (or `passes`) method may be used to perform the validation. - if ($validator->fails()) { - // The given data did not pass validation - } +```php +if ($validator->fails()) { + // The given data did not pass validation +} +``` If validation has failed, you may retrieve the error messages from the validator. - $messages = $validator->messages(); +```php +$messages = $validator->messages(); +``` You may also access an array of the failed validation rules, without messages. To do so, use the `failed` method: - $failed = $validator->failed(); +```php +$failed = $validator->failed(); +``` #### Validating files @@ -78,53 +90,67 @@ After calling the `messages` method on a `Validator` instance, you will receive #### Retrieving the first error message for a field - echo $messages->first('email'); +```php +echo $messages->first('email'); +``` #### Retrieving all error messages for a field - foreach ($messages->get('email') as $message) { - // - } +```php +foreach ($messages->get('email') as $message) { + // +} +``` #### Retrieving all error messages for all fields - foreach ($messages->all() as $message) { - // - } +```php +foreach ($messages->all() as $message) { + // +} +``` #### Determining if messages exist for a field - if ($messages->has('email')) { - // - } +```php +if ($messages->has('email')) { + // +} +``` #### Retrieving an error message with a format - echo $messages->first('email', '

    :message

    '); +```php +echo $messages->first('email', '

    :message

    '); +``` > **NOTE:** By default, messages are formatted using Bootstrap compatible syntax. #### Retrieving all error messages with a format - foreach ($messages->all('
  • :message
  • ') as $message) { - // - } +```php +foreach ($messages->all('
  • :message
  • ') as $message) { + // +} +``` ## Error messages & views Once you have performed validation, you will need an easy way to get the error messages back to your views. This is conveniently handled by Winter. Consider the following routes as an example: - public function onRegister() - { - $rules = []; +```php +public function onRegister() +{ + $rules = []; - $validator = Validator::make(Input::all(), $rules); + $validator = Validator::make(Input::all(), $rules); - if ($validator->fails()) { - return Redirect::to('register')->withErrors($validator); - } + if ($validator->fails()) { + return Redirect::to('register')->withErrors($validator); } +} +``` Note that when validation fails, we pass the `Validator` instance to the Redirect using the `withErrors` method. This method will flash the error messages to the session so that they are available on the next request. @@ -132,17 +158,23 @@ Winter will always check for errors in the session data, and automatically bind So, after redirection, you may utilize the automatically bound `errors` variable in your view: - {{ errors.first('email') }} +```twig +{{ errors.first('email') }} +``` ### Named error bags If you have multiple forms on a single page, you may wish to name the `MessageBag` of errors. This will allow you to retrieve the error messages for a specific form. Simply pass a name as the second argument to `withErrors`: - return Redirect::to('register')->withErrors($validator, 'login'); +```php +return Redirect::to('register')->withErrors($validator, 'login'); +``` You may then access the named `MessageBag` instance from the `$errors` variable: - {{ errors.login.first('email') }} +```twig +{{ errors.login.first('email') }} +``` ## Available validation rules @@ -296,19 +328,27 @@ The field under validation must exist on a given database table. #### Basic usage of exists rule - 'state' => 'exists:states' +```php +'state' => 'exists:states' +``` #### Specifying a custom column name - 'state' => 'exists:states,abbreviation' +```php +'state' => 'exists:states,abbreviation' +``` You may also specify more conditions that will be added as "where" clauses to the query: - 'email' => 'exists:staff,email,account_id,1' +```php +'email' => 'exists:staff,email,account_id,1' +``` Passing `NULL` as a "where" clause value will add a check for a `NULL` database value: - 'email' => 'exists:staff,email,deleted_at,NULL' +```php +'email' => 'exists:staff,email,deleted_at,NULL' +``` #### image @@ -342,7 +382,9 @@ The file under validation must have a MIME type corresponding to one of the list #### Basic usage of MIME rule - 'photo' => 'mimes:jpeg,bmp,png' +```php +'photo' => 'mimes:jpeg,bmp,png' +``` #### min:_value_ @@ -428,21 +470,29 @@ The field under validation must be unique on a given database table. If the `col #### Basic usage of unique rule - 'email' => 'unique:users' +```php +'email' => 'unique:users' +``` #### Specifying a custom column name - 'email' => 'unique:users,email_address' +```php +'email' => 'unique:users,email_address' +``` #### Forcing a unique rule to ignore a given ID - 'email' => 'unique:users,email_address,10' +```php +'email' => 'unique:users,email_address,10' +``` #### Adding additional where clauses You may also specify more conditions that will be added as "where" clauses to the query: - 'email' => 'unique:users,email_address,NULL,id,account_id,1' +```php +'email' => 'unique:users,email_address,NULL,id,account_id,1' +``` In the rule above, only rows with an `account_id` of `1` would be included in the unique check. @@ -458,9 +508,11 @@ The field under validation must be formatted as an URL. In some situations, you may wish to run validation checks against a field **only** if that field is present in the input array. To quickly accomplish this, add the `sometimes` rule to your rule list: - $v = Validator::make($data, [ - 'email' => 'sometimes|required|email', - ]); +```php +$v = Validator::make($data, [ + 'email' => 'sometimes|required|email', +]); +``` In the example above, the `email` field will only be validated if it is present in the `$data` array. @@ -468,22 +520,28 @@ In the example above, the `email` field will only be validated if it is present Sometimes you may wish to require a given field only if another field has a greater value than 100. Or you may need two fields to have a given value only when another field is present. Adding these validation rules doesn't have to be a pain. First, create a `Validator` instance with your _static rules_ that never change: - $v = Validator::make($data, [ - 'email' => 'required|email', - 'games' => 'required|numeric', - ]); +```php +$v = Validator::make($data, [ + 'email' => 'required|email', + 'games' => 'required|numeric', +]); +``` Let's assume our web application is for game collectors. If a game collector registers with our application and they own more than 100 games, we want them to explain why they own so many games. For example, perhaps they run a game re-sell shop, or maybe they just enjoy collecting. To conditionally add this requirement, we can use the `sometimes` method on the `Validator` instance. - $v->sometimes('reason', 'required|max:500', function($input) { - return $input->games >= 100; - }); +```php +$v->sometimes('reason', 'required|max:500', function($input) { + return $input->games >= 100; +}); +``` The first argument passed to the `sometimes` method is the name of the field we are conditionally validating. The second argument is the rules we want to add. If the `Closure` passed as the third argument returns `true`, the rules will be added. This method makes it a breeze to build complex conditional validations. You may even add conditional validations for several fields at once: - $v->sometimes(['reason', 'cost'], 'required', function($input) { - return $input->games >= 100; - }); +```php +$v->sometimes(['reason', 'cost'], 'required', function($input) { + return $input->games >= 100; +}); +``` > **NOTE:** The `$input` parameter passed to your `Closure` will be an instance of `Illuminate\Support\Fluent` and may be used as an object to access your input and files. @@ -492,31 +550,39 @@ The first argument passed to the `sometimes` method is the name of the field we Validating array based form input fields doesn't have to be a pain. You may use "dot notation" to validate attributes within an array. For example, if the incoming HTTP request contains a `photos[profile]` field, you may validate it like so: - $validator = Validator::make(Input::all(), [ - 'photos.profile' => 'required|image', - ]); +```php +$validator = Validator::make(Input::all(), [ + 'photos.profile' => 'required|image', +]); +``` You may also validate each element of an array. For example, to validate that each e-mail in a given array input field is unique, you may do the following: - $validator = Validator::make(Input::all(), [ - 'person.*.email' => 'email|unique:users', - 'person.*.first_name' => 'required_with:person.*.last_name', - ]); +```php +$validator = Validator::make(Input::all(), [ + 'person.*.email' => 'email|unique:users', + 'person.*.first_name' => 'required_with:person.*.last_name', +]); +``` Likewise, you may use the `*` character when specifying your validation messages in your language files, making it a breeze to use a single validation message for array based fields: - 'custom' => [ - 'person.*.email' => [ - 'unique' => 'Each person must have a unique e-mail address', - ] - ], +```php +'custom' => [ + 'person.*.email' => [ + 'unique' => 'Each person must have a unique e-mail address', + ] +], +``` You may also use "array notation" in your validation rules if you wish. These rules will be converted to "dot notation" automatically on validation. - $validator = Validator::make(Input::all(), [ - 'photos[profile]' => 'required|image', - 'person[][email]' => 'email|unique:users', - ]); +```php +$validator = Validator::make(Input::all(), [ + 'photos[profile]' => 'required|image', + 'person[][email]' => 'email|unique:users', +]); +``` ## Custom error messages @@ -525,44 +591,54 @@ If needed, you may use custom error messages for validation instead of the defau #### Passing custom messages into validator - $messages = [ - 'required' => 'The :attribute field is required.', - ]; +```php +$messages = [ + 'required' => 'The :attribute field is required.', +]; - $validator = Validator::make($input, $rules, $messages); +$validator = Validator::make($input, $rules, $messages); +``` > *Note:* The `:attribute` place-holder will be replaced by the actual name of the field under validation. You may also utilize other place-holders in validation messages. #### Other validation placeholders - $messages = [ - 'same' => 'The :attribute and :other must match.', - 'size' => 'The :attribute must be exactly :size.', - 'between' => 'The :attribute must be between :min - :max.', - 'in' => 'The :attribute must be one of the following types: :values', - ]; +```php +$messages = [ + 'same' => 'The :attribute and :other must match.', + 'size' => 'The :attribute must be exactly :size.', + 'between' => 'The :attribute must be between :min - :max.', + 'in' => 'The :attribute must be one of the following types: :values', +]; +``` #### Specifying a custom message for a given attribute Sometimes you may wish to specify a custom error messages only for a specific field: - $messages = [ - 'email.required' => 'We need to know your e-mail address!', - ]; +```php +$messages = [ + 'email.required' => 'We need to know your e-mail address!', +]; +``` #### Specifying custom messages in language files In some cases, you may wish to specify your custom messages in a language file instead of passing them directly to the `Validator`. To do so, add your messages to an array in the `lang/xx/validation.php` language file for your plugin. - return [ - 'required' => 'We need to know your e-mail address!', - 'email.required' => 'We need to know your e-mail address!', - ]; +```php +return [ + 'required' => 'We need to know your e-mail address!', + 'email.required' => 'We need to know your e-mail address!', +]; +``` Then in your call to `Validator::make` use the `Lang:get` to use your custom files. - Validator::make($formValues, $validations, Lang::get('acme.blog::validation')); +```php +Validator::make($formValues, $validations, Lang::get('acme.blog::validation')); +``` ## Custom validation rules @@ -574,15 +650,15 @@ There are a variety of helpful validation rules; however, you may wish to specif The easiest way to register custom validation rules is by adding the `registerValidationRules() : array` method in the [`Plugin.php` registration file](../plugin/registration#registration-methods) for your plugin. This method should return an array where the key is the validator rule name and the value is either a class that extends `Winter\Storm\Validation\Rule` or a callable function. The callable function receives four arguments, the name of the `$attribute` being validated, the `$value` of the attribute and an array of `$parameters` passed to the rule, and the `$validator` instance. ```php - public function registerValidationRules() - { - return [ - 'be_like_bob' => \Winter\Tester\Rules\BeLikeBobRule::class, - 'uppercase' => function ($attribute, $value, $parameters, $validator) { - return strtoupper($value) === $value; - }, - ]; - } +public function registerValidationRules() +{ + return [ + 'be_like_bob' => \Winter\Tester\Rules\BeLikeBobRule::class, + 'uppercase' => function ($attribute, $value, $parameters, $validator) { + return strtoupper($value) === $value; + }, + ]; +} ``` Another way to register custom validation rules is by extending the Validator instance via the `extend` method. In a Winter CMS plugin, this can be added to the `boot()` callback method inside your `Plugin.php` registration file. @@ -594,14 +670,14 @@ You can extend the Validator instance with your custom validation rule as a `Clo If you only need the functionality of a custom rule specified once throughout your plugin or application, you may use a Closure to define the rule. The first parameter defines the name of your rule, and the second parameter provides your Closure. ```php - use Validator; +use Validator; - public function boot() - { - Validator::extend('foo', function($attribute, $value, $parameters) { - return $value == 'foo'; - }); - } +public function boot() +{ + Validator::extend('foo', function($attribute, $value, $parameters) { + return $value == 'foo'; + }); +} ``` The custom validator Closure receives three arguments: the name of the `$attribute` being validated, the `$value` of the attribute, and an array of `$parameters` passed to the rule. @@ -615,9 +691,9 @@ Validator::extend('foo', 'FooValidator@validate'); Once the Validator has been extended with your custom rule, you will need to add it to your rules definition. For example, you may add it to the `$rules` array of your model. ```php - public $rules = [ - 'field' => 'foo' - ]; +public $rules = [ + 'field' => 'foo' +]; ``` #### Using Rule objects @@ -625,39 +701,39 @@ Once the Validator has been extended with your custom rule, you will need to add A `Rule` object represents a single reusable validation rule for your models that implements the `Illuminate\Contracts\Validation\Rule` contract. Each rule object must provide three methods: a `passes` method which determines if a given value passes validation and a `message` method which defines the default fallback error message. `Rule` objects should extend the `Winter\Storm\Validation\Rule` abstract. ```php - ### Nginx configuration @@ -56,59 +60,61 @@ There are small changes required to configure your site in Nginx. Use the following code in **server** section. If you have installed Winter into a subdirectory, replace the first `/` in location directives with the directory Winter was installed under: - location / { - # Let Winter CMS handle everything by default. - # The path not resolved by Winter CMS router will return Winter CMS's 404 page. - # Everything that does not match with the whitelist below will fall into this. - rewrite ^/.*$ /index.php last; - } - - # Pass the PHP scripts to FastCGI server - location ~ ^/index.php { - # Write your FPM configuration here - - } - - # Whitelist - ## Let Winter handle if static file not exists - location ~ ^/favicon\.ico { try_files $uri /index.php; } - location ~ ^/sitemap\.xml { try_files $uri /index.php; } - location ~ ^/robots\.txt { try_files $uri /index.php; } - location ~ ^/humans\.txt { try_files $uri /index.php; } - - # Block access to all dot files and folders except .well-known - location ~ /\.(?!well-known).* { deny all; } - - ## Let nginx return 404 if static file not exists - location ~ ^/storage/app/uploads/public { try_files $uri 404; } - location ~ ^/storage/app/media { try_files $uri 404; } - location ~ ^/storage/app/resized { try_files $uri 404; } - location ~ ^/storage/temp/public { try_files $uri 404; } - - location ~ ^/modules/.*/assets { try_files $uri 404; } - location ~ ^/modules/.*/resources { try_files $uri 404; } - location ~ ^/modules/.*/behaviors/.*/assets { try_files $uri 404; } - location ~ ^/modules/.*/behaviors/.*/resources { try_files $uri 404; } - location ~ ^/modules/.*/widgets/.*/assets { try_files $uri 404; } - location ~ ^/modules/.*/widgets/.*/resources { try_files $uri 404; } - location ~ ^/modules/.*/formwidgets/.*/assets { try_files $uri 404; } - location ~ ^/modules/.*/formwidgets/.*/resources { try_files $uri 404; } - location ~ ^/modules/.*/reportwidgets/.*/assets { try_files $uri 404; } - location ~ ^/modules/.*/reportwidgets/.*/resources { try_files $uri 404; } - - location ~ ^/plugins/.*/.*/assets { try_files $uri 404; } - location ~ ^/plugins/.*/.*/resources { try_files $uri 404; } - location ~ ^/plugins/.*/.*/behaviors/.*/assets { try_files $uri 404; } - location ~ ^/plugins/.*/.*/behaviors/.*/resources { try_files $uri 404; } - location ~ ^/plugins/.*/.*/reportwidgets/.*/assets { try_files $uri 404; } - location ~ ^/plugins/.*/.*/reportwidgets/.*/resources { try_files $uri 404; } - location ~ ^/plugins/.*/.*/formwidgets/.*/assets { try_files $uri 404; } - location ~ ^/plugins/.*/.*/formwidgets/.*/resources { try_files $uri 404; } - location ~ ^/plugins/.*/.*/widgets/.*/assets { try_files $uri 404; } - location ~ ^/plugins/.*/.*/widgets/.*/resources { try_files $uri 404; } - - location ~ ^/themes/.*/assets { try_files $uri 404; } - location ~ ^/themes/.*/resources { try_files $uri 404; } +```nginx +location / { + # Let Winter CMS handle everything by default. + # The path not resolved by Winter CMS router will return Winter CMS's 404 page. + # Everything that does not match with the whitelist below will fall into this. + rewrite ^/.*$ /index.php last; +} + +# Pass the PHP scripts to FastCGI server +location ~ ^/index.php { + # Write your FPM configuration here + +} + +# Whitelist +## Let Winter handle if static file not exists +location ~ ^/favicon\.ico { try_files $uri /index.php; } +location ~ ^/sitemap\.xml { try_files $uri /index.php; } +location ~ ^/robots\.txt { try_files $uri /index.php; } +location ~ ^/humans\.txt { try_files $uri /index.php; } + +# Block access to all dot files and folders except .well-known +location ~ /\.(?!well-known).* { deny all; } + +## Let nginx return 404 if static file not exists +location ~ ^/storage/app/uploads/public { try_files $uri 404; } +location ~ ^/storage/app/media { try_files $uri 404; } +location ~ ^/storage/app/resized { try_files $uri 404; } +location ~ ^/storage/temp/public { try_files $uri 404; } + +location ~ ^/modules/.*/assets { try_files $uri 404; } +location ~ ^/modules/.*/resources { try_files $uri 404; } +location ~ ^/modules/.*/behaviors/.*/assets { try_files $uri 404; } +location ~ ^/modules/.*/behaviors/.*/resources { try_files $uri 404; } +location ~ ^/modules/.*/widgets/.*/assets { try_files $uri 404; } +location ~ ^/modules/.*/widgets/.*/resources { try_files $uri 404; } +location ~ ^/modules/.*/formwidgets/.*/assets { try_files $uri 404; } +location ~ ^/modules/.*/formwidgets/.*/resources { try_files $uri 404; } +location ~ ^/modules/.*/reportwidgets/.*/assets { try_files $uri 404; } +location ~ ^/modules/.*/reportwidgets/.*/resources { try_files $uri 404; } + +location ~ ^/plugins/.*/.*/assets { try_files $uri 404; } +location ~ ^/plugins/.*/.*/resources { try_files $uri 404; } +location ~ ^/plugins/.*/.*/behaviors/.*/assets { try_files $uri 404; } +location ~ ^/plugins/.*/.*/behaviors/.*/resources { try_files $uri 404; } +location ~ ^/plugins/.*/.*/reportwidgets/.*/assets { try_files $uri 404; } +location ~ ^/plugins/.*/.*/reportwidgets/.*/resources { try_files $uri 404; } +location ~ ^/plugins/.*/.*/formwidgets/.*/assets { try_files $uri 404; } +location ~ ^/plugins/.*/.*/formwidgets/.*/resources { try_files $uri 404; } +location ~ ^/plugins/.*/.*/widgets/.*/assets { try_files $uri 404; } +location ~ ^/plugins/.*/.*/widgets/.*/resources { try_files $uri 404; } + +location ~ ^/themes/.*/assets { try_files $uri 404; } +location ~ ^/themes/.*/resources { try_files $uri 404; } +``` ### Lighttpd configuration @@ -119,20 +125,22 @@ If your webserver is running Lighttpd you can use the following configuration to Paste the following code in the editor and change the **host address** and **server.document-root** to match your project. - $HTTP["host"] =~ "domain.example.com" { - server.document-root = "/var/www/example/" - - url.rewrite-once = ( - "^/(plugins|modules/(system|backend|cms))/(([\w-]+/)+|/|)assets/([\w-]+/)+[-\w^&'@{}[\],$=!#().%+~/ ]+\.(jpg|jpeg|gif|png|svg|swf|avi|mpg|mpeg|mp3|flv|ico|css|js|woff|ttf)(\?.*|)$" => "$0", - "^/(system|themes/[\w-]+)/assets/([\w-]+/)+[-\w^&'@{}[\],$=!#().%+~/ ]+\.(jpg|jpeg|gif|png|svg|swf|avi|mpg|mpeg|mp3|flv|ico|css|js|woff|ttf)(\?.*|)$" => "$0", - "^/storage/app/uploads/public/[\w-]+/.*$" => "$0", - "^/storage/app/media/.*$" => "$0", - "^/storage/app/resized/.*$" => "$0", - "^/storage/temp/public/[\w-]+/.*$" => "$0", - "^/(favicon\.ico)$" => "$0", - "(.*)" => "/index.php$1" - ) - } +``` +$HTTP["host"] =~ "domain.example.com" { + server.document-root = "/var/www/example/" + + url.rewrite-once = ( + "^/(plugins|modules/(system|backend|cms))/(([\w-]+/)+|/|)assets/([\w-]+/)+[-\w^&'@{}[\],$=!#().%+~/ ]+\.(jpg|jpeg|gif|png|svg|swf|avi|mpg|mpeg|mp3|flv|ico|css|js|woff|ttf)(\?.*|)$" => "$0", + "^/(system|themes/[\w-]+)/assets/([\w-]+/)+[-\w^&'@{}[\],$=!#().%+~/ ]+\.(jpg|jpeg|gif|png|svg|swf|avi|mpg|mpeg|mp3|flv|ico|css|js|woff|ttf)(\?.*|)$" => "$0", + "^/storage/app/uploads/public/[\w-]+/.*$" => "$0", + "^/storage/app/media/.*$" => "$0", + "^/storage/app/resized/.*$" => "$0", + "^/storage/temp/public/[\w-]+/.*$" => "$0", + "^/(favicon\.ico)$" => "$0", + "(.*)" => "/index.php$1" + ) +} +``` ### IIS configuration @@ -206,35 +214,41 @@ The Winter platform and some marketplace plugins will implement changes in two s You can instruct the platform to prefer test builds from the marketplace by changing the `edgeUpdates` parameter in the `config/cms.php` configuration file. - /* - |-------------------------------------------------------------------------- - | Bleeding edge updates - |-------------------------------------------------------------------------- - | - | If you are developing with Winter, it is important to have the latest - | code base, set this value to 'true' to tell the platform to download - | and use the development copies of core files and plugins. - | - */ - - 'edgeUpdates' => false, +```php +/* +|-------------------------------------------------------------------------- +| Bleeding edge updates +|-------------------------------------------------------------------------- +| +| If you are developing with Winter, it is important to have the latest +| code base, set this value to 'true' to tell the platform to download +| and use the development copies of core files and plugins. +| +*/ + +'edgeUpdates' => false, +``` > **NOTE:** For plugin developers we recommend enabling **Test updates** for your plugins listed on the marketplace, via the Plugin Settings page. > **NOTE:** If using [Composer](../console/commands#console-install-composer) to manage updates, then replace the default Winter CMS requirements in your `composer.json` file with the following in order to download updates directly from the develop branch. - "winter/storm": "dev-develop as 1.0", - "winter/wn-system-module": "dev-develop", - "winter/wn-backend-module": "dev-develop", - "winter/wn-cms-module": "dev-develop", - "laravel/framework": "~6.0", +```json +"winter/storm": "dev-develop as 1.0", +"winter/wn-system-module": "dev-develop", +"winter/wn-backend-module": "dev-develop", +"winter/wn-cms-module": "dev-develop", +"laravel/framework": "~6.0", +``` ### Using a public folder For ultimate security in production environments you may configure your web server to use a **public/** folder to ensure only public files can be accessed. First you will need to spawn a public folder using the `winter:mirror` command. - php artisan winter:mirror public/ +```bash +php artisan winter:mirror public/ +``` This will create a new directory called **public/** in the project's base directory, from here you should modify the webserver configuration to use this new path as the home directory, also known as *wwwroot*. @@ -247,16 +261,18 @@ If you share a server with other users, you should act as if your neighbor's sit You can setup this protection in the file location `config/cms.php` in the section titled **Default permission mask**. - /* - |-------------------------------------------------------------------------- - | Default permission mask - |-------------------------------------------------------------------------- - | - | Specifies a default file and folder permission for newly created objects. - | - */ - - 'defaultMask' => ['file' => '644', 'folder' => '755'], +```php +/* +|-------------------------------------------------------------------------- +| Default permission mask +|-------------------------------------------------------------------------- +| +| Specifies a default file and folder permission for newly created objects. +| +*/ + +'defaultMask' => ['file' => '644', 'folder' => '755'], +``` > **NOTE**: Don't forget to manually check to see if the files are already set to 644, as you may need to go into your cPanel and set them. @@ -335,6 +351,7 @@ The `trustedProxyHeaders` value specifies which headers will be allowed to defin ``` > **NOTE:** Amazon Elastic Load Balancing users must use the `HEADER_X_FORWARDED_AWS_ELB` option to accept the correct headers. +> > ```php > 'trustedProxyHeaders' => Illuminate\Http\Request::HEADER_X_FORWARDED_AWS_ELB > ``` @@ -361,19 +378,21 @@ In both of the above examples, the environment is set to the new value `dev`. Co For example, to use a different MySQL database for the `dev` environment only, create a file called **config/dev/database.php** using this content: - [ - 'mysql' => [ - 'host' => 'localhost', - 'port' => '', - 'database' => 'database', - 'username' => 'root', - 'password' => '' - ] +```php + [ + 'mysql' => [ + 'host' => 'localhost', + 'port' => '', + 'database' => 'database', + 'username' => 'root', + 'password' => '' ] - ]; + ] +]; +``` ### Domain driven environment @@ -382,25 +401,31 @@ Winter supports using an environment detected by a specific hostname. You may pl Using this file contents below, when the application is accessed via **global.website.tld** the environment will be set to `global` and likewise for the others. - [ - 'global.website.tld' => 'global', - 'local.website.tld' => 'local', - ] - ]; +```php + [ + 'global.website.tld' => 'global', + 'local.website.tld' => 'local', + ] +]; +``` ### Converting to DotEnv configuration As an alternative to the [base environment configuration](#base-environment) you may place common values in the environment instead of using configuration files. The config is then accessed using [DotEnv](https://github.com/vlucas/phpdotenv) syntax. Run the `winter:env` command to move common config values to the environment: - php artisan winter:env +```bash +php artisan winter:env +``` This will create an **.env** file in project root directory and modify configuration files to use `env` helper function. The first argument contains the key name found in the environment, the second argument contains an optional default value. - 'debug' => env('APP_DEBUG', true), +```php +'debug' => env('APP_DEBUG', true), +``` Your `.env` file should not be committed to your application's source control, since each developer or server using your application could require a different environment configuration. diff --git a/setup-installation.md b/setup-installation.md index 7fda2b69..dc5aca2b 100644 --- a/setup-installation.md +++ b/setup-installation.md @@ -55,7 +55,7 @@ The [Web Installer](https://github.com/wintercms/web-installer) is the recommend 5. In your web browser, navigate to the URL pointing to that folder, and include `/install.html` at the end of the URL. 6. Follow the instructions given in the installer. -![image](https://github.com/wintercms/docs/blob/main/images/web-installer.jpg?raw=true) {.img-responsive .frame} +![Winter CMS Installer](https://github.com/wintercms/docs/blob/main/images/web-installer.jpg?raw=true) {.img-responsive .frame} ### Troubleshooting installation @@ -85,8 +85,10 @@ There are some things you may need to set up after the installation is complete. If you have used the [Wizard installer](#wizard-installation), for security reasons you should verify the installation files have been deleted. The Winter installer attempts to cleanup after itself, but you should always verify that they have been successfullly removed: - install/ <== Installation directory - install.html <== Installation script +```css + ┣ 📂 install <== Installation directory + ┣ 📜 install.html <== Installation script +``` ### Review configuration diff --git a/themes-development.md b/themes-development.md index eeb01066..4ae6c63d 100644 --- a/themes-development.md +++ b/themes-development.md @@ -24,16 +24,22 @@ The theme information file **theme.yaml** contains the theme description, the au The following fields are supported in the **theme.yaml** file: + +
    + Field | Description ------------- | ------------- -**name** | specifies the theme name, required. -**author** | specifies the author name, required. -**homepage** | specifies the author website URL, required. -**description** | the theme description, required. -**previewImage** | custom preview image, path relative to the theme directory, eg: `assets/images/preview.png`, optional. -**code** | the theme code, optional. The value is used on the Winter CMS marketplace for initializing the theme code value. If the theme code is not provided, the theme directory name will be used as a code. When a theme is installed from the Marketplace, the code is used as the new theme directory name. -**form** | a configuration array or reference to a form field definition file, used for [theme customization](#customization), optional. -**require** | an array of plugin names used for [theme dependencies](#dependencies), optional. +`name` | specifies the theme name, required. +`author` | specifies the author name, required. +`homepage` | specifies the author website URL, required. +`description` | the theme description, required. +`previewImage` | custom preview image, path relative to the theme directory, eg: `assets/images/preview.png`, optional. +`code` | the theme code, optional. The value is used on the Winter CMS marketplace for initializing the theme code value. If the theme code is not provided, the theme directory name will be used as a code. When a theme is installed from the Marketplace, the code is used as the new theme directory name. +`form` | a configuration array or reference to a form field definition file, used for [theme customization](#customization), optional. +`require` | an array of plugin names used for [theme dependencies](#dependencies), optional. Example of the theme information file: @@ -162,7 +168,7 @@ a { color: @link-color } ## Theme dependencies -A theme can depend on plugins by defining a **require** option in the [Theme information file](#theme-information), the option should supply an array of plugin names that are considered requirements. A theme that depends on **Acme.Blog** and **Acme.User** can define this requirement like so: +A theme can depend on plugins by defining a `require` option in the [Theme information file](#theme-information), the option should supply an array of plugin names that are considered requirements. A theme that depends on **Acme.Blog** and **Acme.User** can define this requirement like so: ```yaml name: "Winter CMS Demo" From 1b8a371f8f82bf38a37ce101d667903671769789 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:19:49 +0200 Subject: [PATCH 02/30] Update markup-filter-media.md Co-authored-by: Luke Towers --- markup-filter-media.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup-filter-media.md b/markup-filter-media.md index b07afa7b..1697c23c 100644 --- a/markup-filter-media.md +++ b/markup-filter-media.md @@ -8,6 +8,6 @@ The `| media` filter returns an address relative to the public path of the [medi If the media manager address is `https://cdn.wintercms.com` the above example would output the following: -```twig +```html ``` From 4d343f1f66547e331b8ad24174bd80de722aa569 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:19:57 +0200 Subject: [PATCH 03/30] Update markup-filter-app.md Co-authored-by: Luke Towers --- markup-filter-app.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup-filter-app.md b/markup-filter-app.md index 3a15b4a5..0f495a6f 100644 --- a/markup-filter-app.md +++ b/markup-filter-app.md @@ -8,7 +8,7 @@ The `| app` filter returns an address relative to the public path of the website If the website address is __https://example.com__ the above example would output the following: -```twig +```html ``` From 4244afcfbd60970d49249a12c7324217f952a5ba Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:20:07 +0200 Subject: [PATCH 04/30] Update markup-function-form.md Co-authored-by: Luke Towers --- markup-function-form.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup-function-form.md b/markup-function-form.md index 6b60ad40..322f131a 100644 --- a/markup-function-form.md +++ b/markup-function-form.md @@ -67,7 +67,7 @@ Attributes can be passed in the second argument. The above example would output as the following: -```twig +```html
    ``` From 8b02f99bea31a02fbfc03158b4f40f041f1eedab Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:20:25 +0200 Subject: [PATCH 05/30] Update markup-function-html.md Co-authored-by: Luke Towers --- markup-function-html.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup-function-html.md b/markup-function-html.md index 552e8b0f..0ab20602 100644 --- a/markup-function-html.md +++ b/markup-function-html.md @@ -55,7 +55,7 @@ Obfuscates an e-mail address to prevent spam-bots from sniffing it. For example: ```twig -Email me +Email me Email me From d71f1e8808670d1ba41eac0277cb98deaf77a682 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:20:37 +0200 Subject: [PATCH 06/30] Update markup-function-str.md Co-authored-by: Luke Towers --- markup-function-str.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup-function-str.md b/markup-function-str.md index 77645508..018fc455 100644 --- a/markup-function-str.md +++ b/markup-function-str.md @@ -8,7 +8,7 @@ Functions prefixed with `str_` perform tasks that are useful when dealing with s is the PHP equivalent of the following: -```twig +```php ``` From 7a60344cfbe63a5f477063f71cd368d5612c30d3 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:20:50 +0200 Subject: [PATCH 07/30] Update markup-tag-for.md Co-authored-by: Luke Towers --- markup-tag-for.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup-tag-for.md b/markup-tag-for.md index b4f2b079..68b986ac 100644 --- a/markup-tag-for.md +++ b/markup-tag-for.md @@ -55,7 +55,7 @@ It can also be useful with letters: The `..` operator can take any expression at both sides: ```twig -{% for letter in 'a'|upper..'z'|upper %} +{% for letter in 'a' | upper..'z' | upper %} - {{ letter }} {% endfor %} ``` From a8aa8ab3b8a28d6409963b784ebe7ea78e999bac Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:21:32 +0200 Subject: [PATCH 08/30] Update markup-tag-page.md Co-authored-by: Luke Towers --- markup-tag-page.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup-tag-page.md b/markup-tag-page.md index 4485f0d9..f223e29d 100644 --- a/markup-tag-page.md +++ b/markup-tag-page.md @@ -30,7 +30,7 @@ description="example page" The page rendered with the template would result in: -```twig +```html From 1da937e49b94bf3bb5664cd625429ef06618445b Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:21:46 +0200 Subject: [PATCH 09/30] Update markup-templating.md Co-authored-by: Luke Towers --- markup-templating.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup-templating.md b/markup-templating.md index 4fc69d1a..ec33d41a 100644 --- a/markup-templating.md +++ b/markup-templating.md @@ -55,7 +55,7 @@ Tags can take on many different syntaxes and are listed under the **Tags** secti Filters act as modifiers to variables for a single instance and are applied using a *pipe symbol* followed by the filter name. ```twig -{{ 'string'|filter }} +{{ 'string' | filter }} ``` Filters can take arguments like a function. From 2b461867cb108b0fc8d65df75b0ff0bcd4169e36 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:22:06 +0200 Subject: [PATCH 10/30] Update markup-templating.md Co-authored-by: Luke Towers --- markup-templating.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup-templating.md b/markup-templating.md index ec33d41a..ad66afd6 100644 --- a/markup-templating.md +++ b/markup-templating.md @@ -61,7 +61,7 @@ Filters act as modifiers to variables for a single instance and are applied usin Filters can take arguments like a function. ```twig -{{ price|currency('USD') }} +{{ price | currency('USD') }} ``` Filters can be applied in succession. From 06452fd2587b20f7ab6bdc945936bf997f10b10b Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:22:14 +0200 Subject: [PATCH 11/30] Update services-parser.md Co-authored-by: Luke Towers --- services-parser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services-parser.md b/services-parser.md index 5cbcd385..b0e7b3ad 100644 --- a/services-parser.md +++ b/services-parser.md @@ -433,7 +433,7 @@ Text input for rich content (WYSIWYG). Renders in Twig as ```twig -{{ content|raw }} +{{ content | raw }} ```
    From ab2c92fa53422e9782953c5cdb769c3aad4578b8 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:22:40 +0200 Subject: [PATCH 12/30] Update markup-templating.md Co-authored-by: Luke Towers --- markup-templating.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/markup-templating.md b/markup-templating.md index ad66afd6..b3977271 100644 --- a/markup-templating.md +++ b/markup-templating.md @@ -67,7 +67,7 @@ Filters can take arguments like a function. Filters can be applied in succession. ```twig -{{ 'Winter Glory'|upper|replace({'Winter': 'Morning'}) }} +{{ 'Winter Glory' | upper | replace({'Winter': 'Morning'}) }} ``` Filters are listed under the **Filters** section. From 76d1c619eefdad1927f91bae5ed3ba23358b349b Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:22:56 +0200 Subject: [PATCH 13/30] Update plugin-components.md Co-authored-by: Luke Towers --- plugin-components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-components.md b/plugin-components.md index 3f8e516d..cce9d8b7 100644 --- a/plugin-components.md +++ b/plugin-components.md @@ -202,7 +202,7 @@ public function defineProperties() public function getCountryOptions() { - return ['us'=>'United states', 'ca'=>'Canada']; + return ['us' => 'United states', 'ca' => 'Canada']; } ``` From af8bc572049d57ec02060e9d88499889cb2d1493 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:23:12 +0200 Subject: [PATCH 14/30] Update plugin-components.md Co-authored-by: Luke Towers --- plugin-components.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin-components.md b/plugin-components.md index cce9d8b7..cc3d3f71 100644 --- a/plugin-components.md +++ b/plugin-components.md @@ -236,8 +236,8 @@ public function getStateOptions() $countryCode = Request::input('country'); // Load the country property value from POST $states = [ - 'ca' => ['ab'=>'Alberta', 'bc'=>'British columbia'], - 'us' => ['al'=>'Alabama', 'ak'=>'Alaska'] + 'ca' => ['ab' => 'Alberta', 'bc' => 'British Columbia'], + 'us' => ['al' => 'Alabama', 'ak' => 'Alaska'], ]; return $states[$countryCode]; From d6a82d8a56a14f090cc9033f91c819cec4d6d97f Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:23:25 +0200 Subject: [PATCH 15/30] Update services-response-view.md Co-authored-by: Luke Towers --- services-response-view.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services-response-view.md b/services-response-view.md index 4a7338f8..4483ed2c 100644 --- a/services-response-view.md +++ b/services-response-view.md @@ -69,9 +69,9 @@ Keep in mind that most response methods are chainable, allowing for the fluent b ```php return Response::make($content) - ->header('Content-Type', $type) - ->header('X-Header-One', 'Header Value') - ->header('X-Header-Two', 'Header Value'); + ->header('Content-Type', $type) + ->header('X-Header-One', 'Header Value') + ->header('X-Header-Two', 'Header Value'); ``` A practical example of this could be returning an XML response: From 43b5b6964d46c80dc466d14ec833866990c23d60 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:24:15 +0200 Subject: [PATCH 16/30] Update plugin-components.md Co-authored-by: Luke Towers --- plugin-components.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugin-components.md b/plugin-components.md index cc3d3f71..c7fa0c60 100644 --- a/plugin-components.md +++ b/plugin-components.md @@ -252,13 +252,13 @@ Sometimes components need to create links to the website pages. For example, the ```php public function defineProperties() { - return [ - 'postPage' => [ - 'title' => 'Post page', - 'type' => 'dropdown', - 'default' => 'blog/post' - ] - ]; + return [ + 'postPage' => [ + 'title' => 'Post page', + 'type' => 'dropdown', + 'default' => 'blog/post' + ] + ]; } public function getPostPageOptions() From 3041916c793c47d16506c4e4e0a4f14aee8d0c93 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:25:57 +0200 Subject: [PATCH 17/30] Update plugin-components.md Co-authored-by: Luke Towers --- plugin-components.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-components.md b/plugin-components.md index c7fa0c60..8c2c6fe7 100644 --- a/plugin-components.md +++ b/plugin-components.md @@ -478,7 +478,7 @@ If inside a component partial you need to render another component partial conca If an identical component is called twice on the same page, an `id` property can be used to reference each instance. ```twig -{{__SELF__.id}} +{{ __SELF__.id }} ``` The ID is unique each time the component is displayed. From 49b82cecb9ac6737d09a7b770e315898c47d5c67 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:26:51 +0200 Subject: [PATCH 18/30] Update plugin-scheduling.md Co-authored-by: Luke Towers --- plugin-scheduling.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin-scheduling.md b/plugin-scheduling.md index 12bf947c..09f4200d 100644 --- a/plugin-scheduling.md +++ b/plugin-scheduling.md @@ -116,8 +116,8 @@ The scheduler provides several convenient methods for working with the output ge ```php $schedule->command('emails:send') - ->daily() - ->sendOutputTo($filePath); + ->daily() + ->sendOutputTo($filePath); ``` Using the `emailOutputTo` method, you may e-mail the output to an e-mail address of your choice. Note that the output must first be sent to a file using the `sendOutputTo` method. Also before e-mailing the output of a task, you should configure [mail services](../services/mail): From 038d0608942a6da8d739b4876bb59d49bb359225 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:27:02 +0200 Subject: [PATCH 19/30] Update plugin-scheduling.md Co-authored-by: Luke Towers --- plugin-scheduling.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin-scheduling.md b/plugin-scheduling.md index 09f4200d..64e255db 100644 --- a/plugin-scheduling.md +++ b/plugin-scheduling.md @@ -124,9 +124,9 @@ Using the `emailOutputTo` method, you may e-mail the output to an e-mail address ```php $schedule->command('foo') - ->daily() - ->sendOutputTo($filePath) - ->emailOutputTo('foo@example.com'); + ->daily() + ->sendOutputTo($filePath) + ->emailOutputTo('foo@example.com'); ``` > **NOTE:** The `emailOutputTo` and `sendOutputTo` methods are exclusive to the `command` method and are not supported for `call`. From 1b54e42d181e18c49c667668cb4940b86385a400 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:27:15 +0200 Subject: [PATCH 20/30] Update plugin-scheduling.md Co-authored-by: Luke Towers --- plugin-scheduling.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugin-scheduling.md b/plugin-scheduling.md index 64e255db..f1a97b87 100644 --- a/plugin-scheduling.md +++ b/plugin-scheduling.md @@ -138,13 +138,13 @@ Using the `before` and `after` methods, you may specify code to be executed befo ```php $schedule->command('emails:send') - ->daily() - ->before(function () { - // Task is about to start... - }) - ->after(function () { - // Task is complete... - }); + ->daily() + ->before(function () { + // Task is about to start... + }) + ->after(function () { + // Task is complete... + }); ``` #### Pinging URLs From d56146dbfbeb44796432e324af64993024b4e3dd Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:27:46 +0200 Subject: [PATCH 21/30] Update plugin-scheduling.md Co-authored-by: Luke Towers --- plugin-scheduling.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin-scheduling.md b/plugin-scheduling.md index f1a97b87..e99084ae 100644 --- a/plugin-scheduling.md +++ b/plugin-scheduling.md @@ -153,9 +153,9 @@ Using the `pingBefore` and `thenPing` methods, the scheduler can automatically p ```php $schedule->command('emails:send') - ->daily() - ->pingBefore($url) - ->thenPing($url); + ->daily() + ->pingBefore($url) + ->thenPing($url); ``` > You need to install [Drivers plugin](https://wintercms.com/plugin/winter-drivers) before you can use either the `pingBefore($url)` or `thenPing($url)` features. From a74a89fbfb2c1007840801ea75f02e5ddb5ef420 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:28:04 +0200 Subject: [PATCH 22/30] Update services-mail.md Co-authored-by: Luke Towers --- services-mail.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/services-mail.md b/services-mail.md index 6def2baf..38ba898c 100644 --- a/services-mail.md +++ b/services-mail.md @@ -319,13 +319,12 @@ Optionally, mail views can be [registered in the Plugin registration file](#mail Mail views reside in the file system and the code used represents the path to the view file. For example sending mail with the code **author.plugin::mail.message** would use the content in following file: ```css -📂 plugins <=== Plugins directory - ┗ 📂 author <=== "author" segment - ┗ 📂 plugin <=== "plugin" segment - ┗ 📂 views <=== View directory - ┗ 📂 mail <=== "mail" segment - ┗ 📜 message.htm <=== "message" segment -``` +📂 plugins <=== Plugins directory +┗ 📂 author <=== "author" segment + ┗ 📂 plugin <=== "plugin" segment + ┗ 📂 views <=== View directory + ┗ 📂 mail <=== "mail" segment + ┗ 📜 message.htm <=== "message" segment The content inside a mail view file can include up to 3 sections: **configuration**, **plain text**, and **HTML markup**. Sections are separated with the `==` sequence. For example: From 7b2319566a0bd4190a0d07770cfcc402373bd3a2 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:28:20 +0200 Subject: [PATCH 23/30] Update services-parser.md Co-authored-by: Luke Towers --- services-parser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services-parser.md b/services-parser.md index b0e7b3ad..fbe013f8 100644 --- a/services-parser.md +++ b/services-parser.md @@ -40,7 +40,7 @@ $html = Markdown::parse($markdown); You may also use the `|md` filter for [parsing Markdown in your frontend markup](../markup/filter-md). ```twig -{{ '**Text** is bold.'|md }} +{{ '**Text** is bold.' | md }} ``` From d8b3c54436d16ae161232f17a35cf33901099bb7 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:28:31 +0200 Subject: [PATCH 24/30] Update services-parser.md Co-authored-by: Luke Towers --- services-parser.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services-parser.md b/services-parser.md index fbe013f8..5452f96d 100644 --- a/services-parser.md +++ b/services-parser.md @@ -103,10 +103,10 @@ The array can be iterated using the following syntax: YAML ("YAML Ain't Markup Language") is a configuration format, similar to Markdown it was designed to be an easy-to-read and easy-to-write format that converts to a PHP array. It is used practically everywhere for the backend development of Winter, such as [form field](../backend/forms#form-fields) and [list column](../backend/lists##list-columns) definitions. An example of some YAML: ```yaml -receipt: Acme Purchase Invoice -date: 2015-10-02 +receipt: Acme Purchase Invoice +date: 2015-10-02 user: - name: Joe + name: Joe surname: Blogs ``` From 3401266e979913453f091208ad68801b375c1b5b Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:28:43 +0200 Subject: [PATCH 25/30] Update services-parser.md Co-authored-by: Luke Towers --- services-parser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services-parser.md b/services-parser.md index 5452f96d..a665abf4 100644 --- a/services-parser.md +++ b/services-parser.md @@ -325,7 +325,7 @@ Text input for Markdown content. Renders in Twig as ```twig -{{ content|md }} +{{ content | md }} ```
    From 686c6d4285de03c79e2c653eac1a9b1893d266c3 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:28:55 +0200 Subject: [PATCH 26/30] Update services-parser.md Co-authored-by: Luke Towers --- services-parser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services-parser.md b/services-parser.md index a665abf4..72d3ecb7 100644 --- a/services-parser.md +++ b/services-parser.md @@ -339,7 +339,7 @@ File selector for media library items. This tag value will contain the relative Renders in Twig as ```twig -{{ logo|media }} +{{ logo | media }} ```
    From bbcb32bc006cd87aba02800dc7fb38161ad3d1f9 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:29:06 +0200 Subject: [PATCH 27/30] Update services-parser.md Co-authored-by: Luke Towers --- services-parser.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services-parser.md b/services-parser.md index 72d3ecb7..48ecdf0b 100644 --- a/services-parser.md +++ b/services-parser.md @@ -366,7 +366,7 @@ Renders in Twig as ```twig {% for fields in repeater %}

    {{ fields.title }}

    -

    {{ fields.content|raw }}

    +

    {{ fields.content | raw }}

    {% endfor %} ``` From 28fb8783cb274a278c935cb7b3845b49e7863a6f Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:29:22 +0200 Subject: [PATCH 28/30] Update services-response-view.md Co-authored-by: Luke Towers --- services-response-view.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services-response-view.md b/services-response-view.md index 4483ed2c..2ad8998b 100644 --- a/services-response-view.md +++ b/services-response-view.md @@ -122,7 +122,7 @@ If you would like to create a JSONP response, you may use the `json` method in a ```php return Response::json(['name' => 'Steve', 'state' => 'CA']) - ->setCallback(Input::get('callback')); + ->setCallback(Input::get('callback')); ``` From 7d916fdc352b3065da3d9eeb95142e9d427cbc09 Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:29:33 +0200 Subject: [PATCH 29/30] Update services-response-view.md Co-authored-by: Luke Towers --- services-response-view.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services-response-view.md b/services-response-view.md index 2ad8998b..4c64f600 100644 --- a/services-response-view.md +++ b/services-response-view.md @@ -218,7 +218,7 @@ A simple view could look something like this: Views can also be parsed using PHP templating by using the `.php` extension: -```twig +```php From 9021b7f949e8687f8ea08c1beffa919e17aafb5f Mon Sep 17 00:00:00 2001 From: WebVPF <61043464+WebVPF@users.noreply.github.com> Date: Tue, 4 Jan 2022 23:29:44 +0200 Subject: [PATCH 30/30] Update services-response-view.md Co-authored-by: Luke Towers --- services-response-view.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services-response-view.md b/services-response-view.md index 4c64f600..86611117 100644 --- a/services-response-view.md +++ b/services-response-view.md @@ -276,7 +276,7 @@ The sub-view can then be rendered from the parent view:

    Hello!

    - {{ child|raw }} + {{ child | raw }} ```