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:
- 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:
- 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 %}
- 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) %}
- My content.
-
+```twig
+description="example layout"
+==
+
+
+ {% placeholder head %}
+
+
+ {% page %}
+ ...
+```
+
+```twig
+description="example page"
+==
+{% put head %}
+ My content.
+```
+
The page rendered with the template would result in:
-
-
- My content.
- ...
-
\ No newline at end of file
+```html
+
+
+ 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 %}
+```
Country: {{ country }}, city: {{ city }}.
+```twig
+Country: {{ country }}, city: {{ city }}.
+```
-
- {% 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..b3977271 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' %}
-
Test Environment
+
Test Environment
- {% 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 c969ac38..3c68ba34 100644
--- a/plugin-components.md
+++ b/plugin-components.md
@@ -24,14 +24,18 @@
Components files and directories reside in the **/components** subdirectory of a plugin directory. Each component has a PHP file defining the component class and an optional component partials directory. The component partials directory name matches the component class name written in lowercase. An example of a component directory structure:
- plugins/
- acme/
- myplugin/
- components/
- componentname/ <=== Component partials directory
- default.htm <=== Component default markup (optional)
- ComponentName.php <=== Component class file
- Plugin.php
+```css
+π plugins
+ β π acme
+ β π myplugin
+ β£ π components
+ β β£ π componentname <=== Component partials directory
+ β β β π default.htm <=== Component default markup (optional)
+ β β£ π partials <=== Any partials shared by more than one component in the plugin
+ β β β π partialname.htm
+ β β π ComponentName.php <=== Component class file
+ β π Plugin.php
+```
Components must be [registered in the Plugin registration class](#component-registration) with the `registerComponents` method.
@@ -40,51 +44,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).
@@ -93,188 +105,238 @@ 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.
-**description** | required, the property description, it is used by the component Inspector in the CMS backend.
-**default** | optional, the default property value to use when the component is added to a page or layout in the CMS backend.
-**type** | optional, specifies the property type. The type defines the way how the property is displayed in the Inspector. Currently supported types are **string**, **checkbox**, **dropdown** and **set**. Default value: **string**.
-**validationPattern** | optional Regular Expression to use when a user enters the property value in the Inspector. The validation can be used only with **string** properties.
-**validationMessage** | optional error message to display if the validation fails.
-**required** | optional, forces field to be filled. Uses validationMessage when left empty.
-**placeholder** | optional placeholder for string and dropdown properties.
-**options** | optional array of options for dropdown properties.
-**depends** | an array of property names a dropdown property depends on. See the [dropdown properties](#dropdown-properties) below.
-**group** | an optional group name. Groups create sections in the Inspector simplifying the user experience. Use a same group name in multiple properties to combine them.
-**showExternalParam** | specifies visibility of the External Parameter editor for the property in the Inspector. Default value: **true**.
+`title` | required, the property title, it is used by the component Inspector in the CMS backend.
+`description` | required, the property description, it is used by the component Inspector in the CMS backend.
+`default` | optional, the default property value to use when the component is added to a page or layout in the CMS backend.
+`type` | optional, specifies the property type. The type defines the way how the property is displayed in the Inspector. Currently supported types are `string`, `checkbox`, `dropdown` and `set`. Default value: `string`.
+`validationPattern` | optional Regular Expression to use when a user enters the property value in the Inspector. The validation can be used only with `string` properties.
+`validationMessage` | optional error message to display if the validation fails.
+`required` | optional, forces field to be filled. Uses validationMessage when left empty.
+`placeholder` | optional placeholder for string and dropdown properties.
+`options` | optional array of options for dropdown properties.
+`depends` | an array of property names a dropdown property depends on. See the [dropdown properties](#dropdown-properties) below.
+`group` | an optional group name. Groups create sections in the Inspector simplifying the user experience. Use a same group name in multiple properties to combine them.
+`showExternalParam` | specifies visibility of the External Parameter editor for the property in the Inspector. Default value: `true`.
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:
+A `dropdown` allows you to select a single value from a series of options. A `set` allows you to select multiple values from a series of options.
- public function defineProperties()
- {
- return [
- 'units' => [
- 'title' => 'Units',
- 'type' => 'dropdown',
- 'default' => 'imperial',
- 'placeholder' => 'Select units',
- 'options' => ['metric'=>'Metric', 'imperial'=>'Imperial']
- ]
- ];
- }
+The option list for `dropdown` and `set` properties can be static or dynamic. Static options are defined with the `options` property for dropdowns and the `items` property for sets. Example:
-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:
+```php
+public function defineProperties()
+{
+ return [
+ 'units' => [
+ 'title' => 'Units',
+ 'type' => 'dropdown',
+ 'default' => 'imperial',
+ 'placeholder' => 'Select units',
+ 'options' => ['metric'=>'Metric', 'imperial'=>'Imperial']
+ ],
+ 'favoriteFruits' => [
+ 'title' => 'Favorite fruits',
+ 'type' => 'set',
+ 'items' => [
+ 'apples' => 'Apples',
+ 'bananas' => 'Bananas',
+ 'cantaloupes' => 'Cantaloupes',
+ ],
+ 'default' => ['bananas', 'cantaloupes'],
+ ],
+ ];
+}
+```
- public function defineProperties()
- {
- return [
- 'country' => [
- 'title' => 'Country',
- 'type' => 'dropdown',
- 'default' => 'us'
- ]
- ];
- }
+The list of options or items could be fetched dynamically from the server when the Inspector is displayed. If the `options` parameter is omitted for dropdowns or the `items` parameter is omitted for sets, the list is considered dynamic. The component class must define a method returning this 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 getCountryOptions()
- {
- return ['us'=>'United states', 'ca'=>'Canada'];
- }
+```php
+public function defineProperties()
+{
+ return [
+ 'country' => [
+ 'title' => 'Country',
+ 'type' => 'dropdown',
+ 'default' => 'us'
+ ]
+ ];
+}
-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 getCountryOptions()
+{
+ return ['us' => 'United states', 'ca' => 'Canada'];
+}
+```
- public function defineProperties()
- {
- return [
- 'country' => [
- 'title' => 'Country',
- 'type' => 'dropdown',
- 'default' => 'us'
- ],
- 'state' => [
- 'title' => 'State',
- 'type' => 'dropdown',
- 'default' => 'dc',
- 'depends' => ['country'],
- 'placeholder' => 'Select a state'
- ]
- ];
- }
+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:
+
+```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
@@ -296,44 +358,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.
@@ -344,100 +414,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 de5c2c35..da132dd4 100644
--- a/plugin-localization.md
+++ b/plugin-localization.md
@@ -13,60 +13,71 @@ Plugins can have localization files in the **lang** subdirectory of the plugin d
Below is an example of the plugin's lang directory:
- plugins/
- acme/
- todo/ <=== Plugin directory
- lang/ <=== Localization directory
- en/ <=== Language directory
- lang.php <=== Localization file
- fr/
- lang.php
-
+```css
+π plugins
+ β π acme
+ β π todo <=== Plugin directory
+ β π lang <=== Localization directory
+ β£ π en <=== Language directory
+ β β π lang.php <=== Localization file
+ β π fr
+ β π lang.php
+```
The **lang.php** file should define and return an array of any depth, for example:
- [
- 'name' => 'Winter CMS',
- 'tagline' => 'Getting back to basics'
- ]
- ];
+return [
+ 'app' => [
+ '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
System users can override plugin localization strings without altering the plugins' files. This is done by adding localization files to the **lang** directory. For example, to override the lang.php file of the **acme/blog** plugin you should create the file in the following location:
- lang/ <=== App localization directory
- en/ <=== Language directory
- acme/ <=== Plugin / Module directory
- blog/ <===^
- lang.php <=== Localization override file
+```css
+π lang <=== App localization directory
+ β π en <=== Language directory
+ β π acme <=== Plugin / Module directory
+ β π blog <===^
+ β π lang.php <=== Localization override file
+```
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 c5fbbba7..700deb61 100644
--- a/plugin-registration.md
+++ b/plugin-registration.md
@@ -45,9 +45,9 @@ The simplest plugins only require the **Plugin.php** file described below.
```css
π plugins
- β£ π myauthor /* Author name */
- β β£ π myplugin /* Plugin name */
- β β β π Plugin.php /* Plugin registration file, required */
+ β π myauthor /* Author name */
+ β π myplugin /* Plugin name */
+ β π Plugin.php /* Plugin registration file, required */
```
@@ -59,20 +59,21 @@ The following is an example of what most plugins would end up looking like when
```css
π plugins
- β£ π myauthor /* Author name */
- β β£ π myplugin /* Plugin name */
- β β β£ π assets /* CSS, JavaScript and image assets for pages and components */
- β β β£ π controllers /* Backend controllers */
- β β β£ π lang /* Localization files */
- β β β β π en /* Specific locale folder */
- β β β β β π lang.php /* Translations */
- β β β£ π models /* Models */
- β β β£ π updates /* Database migrations */
- β β β β π version.yaml /* Changelog */
- β β β£ π views /* Custom view files */
- β β β β π mail /* Custom mail templates */
- β β β£ π README.md /* Documentation describing the purpose of the plugin */
- β β β π Plugin.php /* Plugin registration class */
+ β π myauthor /* Author name */
+ β π myplugin /* Plugin name */
+ β£ π assets /* CSS, JavaScript and image assets for pages and components */
+ β£ π components /* Frontend components */
+ β£ π controllers /* Backend controllers */
+ β£ π lang /* Localization files */
+ β β π en /* Specific locale folder */
+ β β π lang.php /* Translations */
+ β£ π models /* Models */
+ β£ π updates /* Database migrations */
+ β β π version.yaml /* Changelog */
+ β£ π views /* Custom view files */
+ β β π mail /* Custom mail templates */
+ β£ π README.md /* Documentation describing the purpose of the plugin */
+ β π Plugin.php /* Plugin registration class */
```
@@ -82,62 +83,67 @@ The following is an example of what a complex plugin could look like when using
```css
π plugins
- β£ π myauthor /* Author name */
- β β£ π myplugin /* Plugin name */
- β β β£ π assets /* CSS, JavaScript and image assets for pages and components */
- β β β β£ π css
- β β β β£ π favicons
- β β β β£ π images
- β β β β£ π js
- β β β β π scss
- β β β£ π behaviors /* Any custom behaviors provided by the plugin */
- β β β£ π classes /* Any custom classes provided by the plugin */
- β β β£ π config /* Configuration files */
- β β β β π config.php
- β β β£ π console /* Any custom CLI commands provided by the plugin */
- β β β£ π controllers /* Backend controllers */
- β β β β£ π records /* Directory for the view and configuration files for the given controller */
- β β β β β£ π _list_toolbar.htm /* List toolbar partial file */
- β β β β β£ π config_filter.yaml /* Configuration for the Filter widget present on the controller lists */
- β β β β β£ π config_form.yaml /* Configuration for the Form widget present on the controller */
- β β β β β£ π config_importexport.yaml /* Configuration for the Import/Export behavior */
- β β β β β£ π config_list.yaml /* Configuration for the Lists widget present on the controller */
- β β β β β£ π config_relation.yaml /* Configuration for the RelationController behavior */
- β β β β β£ π create.htm /* View file for the create action */
- β β β β β£ π index.htm /* View file for the index action */
- β β β β β£ π preview.htm /* View file for the preview action */
- β β β β β π update.htm /* View file for the update action */
- β β β β£ π Records.php /* Backend controller for the Record model */
- β β β£ π docs /* Any plugin-specific documentation should live here */
- β β β£ π formwidgets /* Any custom FormWidgets provided by the plugin */
- β β β£ π lang /* Localization files */
- β β β β π en /* Specific locale folder */
- β β β β β π lang.php /* Translations for that locale */
- β β β£ π layouts /* Any custom backend layouts used by the plugin */
- β β β£ π models /* Models provided by the plugin */
- β β β β£ π record /* Directory containing configuration files specific to that model */
- β β β β β£ π columns.yaml /* Configuration file used for the Lists widget */
- β β β β β π fields.yaml /* Configuration file used for the Form widget */
- β β β β£ π Record.php /* Model class for the Record model */
- β β β£ π partials /* Any custom partials used by the plugin */
- β β β£ π reportwidgets /* Any custom ReportWidgets provided by the plugin */
- β β β£ π tests /* Test suite for the plugin */
- β β β£ π traits /* Any custom Traits provided by the plugin */
- β β β£ π updates /* Database migrations */
- β β β β β£ π v1.0.0 /* Migrations for a specific version of the plugin */
- β β β β β β π create_records_table.php /* Database migration file, referenced in version.yaml */
- β β β β π version.yaml /* Changelog */
- β β β£ π views /* Custom view files */
- β β β β π mail /* Custom mail templates provided by the plugin */
- β β β£ π widgets /* Any custom Widgets provided by the plugin */
- β β β£ π LICENSE /* License file */
- β β β£ π README.md /* Documentation describing the purpose of the plugin */
- β β β£ π Plugin.php /* Plugin registration file */
- β β β£ π composer.json /* Composer file to manage dependencies for the plugin */
- β β β£ π helpers.php /* Global helpers provided by the plugin loaded via composer.json */
- β β β£ π phpunit.xml /* Unit testing configuration */
- β β β£ π plugin.yaml /* Simplified plugin registration configuration YAML file, used by Builder plugin */
- β β β π routes.php /* Any custom routes provided by the plugin */
+ β π myauthor /* Author name */
+ β π myplugin /* Plugin name */
+ β£ π assets /* CSS, JavaScript and image assets for pages and components */
+ β β£ π css
+ β β£ π favicons
+ β β£ π images
+ β β£ π js
+ β β π scss
+ β£ π behaviors /* Any custom behaviors provided by the plugin */
+ β£ π classes /* Any custom classes provided by the plugin */
+ β£ π components /* Components frontend */
+ β β£ π record /* Folder for the Record component's partials */
+ β β β π default.htm /* The default partial that's rendered by the component */
+ β β£ π partials /* Any partials shared by more than one component in the plugin */
+ β β π Record.php /* Record Component that probably handles retrieving and displaying a single record */
+ β£ π config /* Configuration files */
+ β β π config.php
+ β£ π console /* Any custom CLI commands provided by the plugin */
+ β£ π controllers /* Backend controllers */
+ β β£ π records /* Directory for the view and configuration files for the given controller */
+ β β β£ π _list_toolbar.htm /* List toolbar partial file */
+ β β β£ π config_filter.yaml /* Configuration for the Filter widget present on the controller lists */
+ β β β£ π config_form.yaml /* Configuration for the Form widget present on the controller */
+ β β β£ π config_importexport.yaml /* Configuration for the Import/Export behavior */
+ β β β£ π config_list.yaml /* Configuration for the Lists widget present on the controller */
+ β β β£ π config_relation.yaml /* Configuration for the RelationController behavior */
+ β β β£ π create.htm /* View file for the create action */
+ β β β£ π index.htm /* View file for the index action */
+ β β β£ π preview.htm /* View file for the preview action */
+ β β β π update.htm /* View file for the update action */
+ β β π Records.php /* Backend controller for the Record model */
+ β£ π docs /* Any plugin-specific documentation should live here */
+ β£ π formwidgets /* Any custom FormWidgets provided by the plugin */
+ β£ π lang /* Localization files */
+ β β π en /* Specific locale folder */
+ β β π lang.php /* Translations for that locale */
+ β£ π layouts /* Any custom backend layouts used by the plugin */
+ β£ π models /* Models provided by the plugin */
+ β β£ π record /* Directory containing configuration files specific to that model */
+ β β β£ π columns.yaml /* Configuration file used for the Lists widget */
+ β β β π fields.yaml /* Configuration file used for the Form widget */
+ β β π Record.php /* Model class for the Record model */
+ β£ π partials /* Any custom partials used by the plugin */
+ β£ π reportwidgets /* Any custom ReportWidgets provided by the plugin */
+ β£ π tests /* Test suite for the plugin */
+ β£ π traits /* Any custom Traits provided by the plugin */
+ β£ π updates /* Database migrations */
+ β β β π v1.0.0 /* Migrations for a specific version of the plugin */
+ β β β π create_records_table.php /* Database migration file, referenced in version.yaml */
+ β β π version.yaml /* Changelog */
+ β£ π views /* Custom view files */
+ β β π mail /* Custom mail templates provided by the plugin */
+ β£ π widgets /* Any custom Widgets provided by the plugin */
+ β£ π LICENSE /* License file */
+ β£ π README.md /* Documentation describing the purpose of the plugin */
+ β£ π Plugin.php /* Plugin registration file */
+ β£ π composer.json /* Composer file to manage dependencies for the plugin */
+ β£ π helpers.php /* Global helpers provided by the plugin loaded via composer.json */
+ β£ π phpunit.xml /* Unit testing configuration */
+ β£ π plugin.yaml /* Simplified plugin registration configuration YAML file, used by Builder plugin */
+ β π routes.php /* Any custom routes provided by the plugin */
```
@@ -190,38 +196,50 @@ class Plugin extends \System\Classes\PluginBase
The following methods are supported in the plugin registration class:
+
+
+
Method | Description
------------- | -------------
-**pluginDetails()** | returns information about the plugin.
-**register()** | register method, called when the plugin is first registered.
-**boot()** | boot method, called right before the request route.
-**registerComponents()** | registers any [frontend components](components#component-registration) used by this plugin.
-**registerFormWidgets()** | registers any [backend form widgets](../backend/widgets#form-widget-registration) supplied by this plugin.
-**registerListColumnTypes()** | registers any [custom list column types](../backend/lists#custom-column-types) supplied by this plugin.
-**registerMailLayouts()** | registers any [mail view layouts](../services/mail#mail-template-registration) supplied by this plugin.
-**registerMailPartials()** | registers any [mail view partials](../services/mail#mail-template-registration) supplied by this plugin.
-**registerMailTemplates()** | registers any [mail view templates](../services/mail#mail-template-registration) supplied by this plugin.
-**registerMarkupTags()** | registers [additional markup tags](#extending-twig) that can be used in the CMS.
-**registerNavigation()** | registers [backend navigation menu items](#navigation-menus) for this plugin.
-**registerPermissions()** | registers any [backend permissions](../backend/users#permission-registration) used by this plugin.
-**registerReportWidgets()** | registers any [backend report widgets](../backend/widgets#report-widget-registration), including the dashboard widgets.
-**registerSchedule()** | registers [scheduled tasks](../plugin/scheduling#defining-schedules) that are executed on a regular basis.
-**registerSettings()** | registers any [backend configuration links](settings#link-registration) used by this plugin.
-**registerValidationRules()** | registers any [custom validators](../services/validation#custom-validation-rules) supplied by this plugin.
+`pluginDetails()` | returns information about the plugin.
+`register()` | register method, called when the plugin is first registered.
+`boot()` | boot method, called right before the request route.
+`registerComponents()` | registers any [frontend components](components#component-registration) used by this plugin.
+`registerFormWidgets()` | registers any [backend form widgets](../backend/widgets#form-widget-registration) supplied by this plugin.
+`registerListColumnTypes()` | registers any [custom list column types](../backend/lists#custom-column-types) supplied by this plugin.
+`registerMailLayouts()` | registers any [mail view layouts](../services/mail#mail-template-registration) supplied by this plugin.
+`registerMailPartials()` | registers any [mail view partials](../services/mail#mail-template-registration) supplied by this plugin.
+`registerMailTemplates()` | registers any [mail view templates](../services/mail#mail-template-registration) supplied by this plugin.
+`registerMarkupTags()` | registers [additional markup tags](#extending-twig) that can be used in the CMS.
+`registerNavigation()` | registers [backend navigation menu items](#navigation-menus) for this plugin.
+`registerPermissions()` | registers any [backend permissions](../backend/users#permission-registration) used by this plugin.
+`registerReportWidgets()` | registers any [backend report widgets](../backend/widgets#report-widget-registration), including the dashboard widgets.
+`registerSchedule()` | registers [scheduled tasks](../plugin/scheduling#defining-schedules) that are executed on a regular basis.
+`registerSettings()` | registers any [backend configuration links](settings#link-registration) used by this plugin.
+`registerValidationRules()` | registers any [custom validators](../services/validation#custom-validation-rules) supplied by this plugin.
### Basic plugin information
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.
-**description** | the plugin description, required.
-**author** | the plugin author name, required.
-**icon** | a name of the plugin icon. The full list of available icons can be found in the [UI documentation](../ui/icon). Any icon names provided by this font are valid, for example **icon-glass**, **icon-music**. This key is required if `iconSvg` is not set.
-**iconSvg** | an SVG icon to be used in place of the standard icon. The SVG icon should be a rectangle and can support colors. This key is required if `icon` is not set.
-**homepage** | a link to the author's website address, optional.
+`name` | the plugin name, required.
+`description` | the plugin description, required.
+`author` | the plugin author name, required.
+`icon` | a name of the plugin icon. The full list of available icons can be found in the [UI documentation](../ui/icon). Any icon names provided by this font are valid, for example **icon-glass**, **icon-music**. This key is required if `iconSvg` is not set.
+`iconSvg` | an SVG icon to be used in place of the standard icon. The SVG icon should be a rectangle and can support colors. This key is required if `icon` is not set.
+`homepage` | a link to the author's website address, optional.
## Routing and initialization
@@ -336,6 +354,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,19 +421,25 @@ 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.
-**icon** | an icon name from the [Winter CMS icon collection](../ui/icon), optional.
-**iconSvg** | an SVG icon to be used in place of the standard icon, the SVG icon should be a rectangle and can support colors, optional.
-**url** | the URL the menu item should point to (ex. `Backend::url('author/plugin/controller/action')`, required.
-**counter** | a numeric value to output near the menu icon. The value should be a number or a callable returning a number, optional.
-**counterLabel** | a string value to describe the numeric reference in counter, optional.
-**badge** | a string value to output in place of the counter, the value should be a string and will override the badge property if set, optional.
-**attributes** | an associative array of attributes and values to apply to the menu item, optional.
-**permissions** | an array of permissions the backend user must have in order to view the menu item (Note: direct access of URLs still requires separate permission checks), optional.
-**code** | a string value that acts as an unique identifier for that menu option. **NOTE**: This is a system generated value and should not be provided when registering the navigation items.
-**owner** | a string value that specifies the menu items owner plugin or module in the format "Author.Plugin". **NOTE**: This is a system generated value and should not be provided when registering the navigation items.
+`label` | specifies the menu label localization string key, required.
+`icon` | an icon name from the [Winter CMS icon collection](../ui/icon), optional.
+`iconSvg` | an SVG icon to be used in place of the standard icon, the SVG icon should be a rectangle and can support colors, optional.
+`url` | the URL the menu item should point to (ex. `Backend::url('author/plugin/controller/action')`, required.
+`counter` | a numeric value to output near the menu icon. The value should be a number or a callable returning a number, optional.
+`counterLabel` | a string value to describe the numeric reference in counter, optional.
+`badge` | a string value to output in place of the counter, the value should be a string and will override the badge property if set, optional.
+`attributes` | an associative array of attributes and values to apply to the menu item, optional.
+`permissions` | an array of permissions the backend user must have in order to view the menu item (Note: direct access of URLs still requires separate permission checks), optional.
+`code` | a string value that acts as an unique identifier for that menu option. **NOTE**: This is a system generated value and should not be provided when registering the navigation items.
+`owner` | a string value that specifies the menu items owner plugin or module in the format "Author.Plugin". **NOTE**: This is a system generated value and should not be provided when registering the navigation items.
## Registering middleware
@@ -425,7 +457,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.
@@ -443,16 +475,22 @@ public function boot()
By default plugins are restricted from accessing certain areas of the system. This is to prevent critical errors that may lock an administrator out from the backend. When these areas are accessed without elevated permissions, the `boot` and `register` [initialization methods](#routing-initialization) for the plugin will not fire.
+
+
+
Request | Description
------------- | -------------
-**/combine** | the asset combiner generator URL
-**/backend/system/updates** | the site updates context
-**/backend/system/install** | the installer path
-**/backend/backend/auth** | the backend authentication path (login, logout)
-**winter:up** | the CLI command that runs all pending migrations
-**winter:update** | the CLI command that triggers the update process
-**winter:env** | the CLI command that converts configuration files to environment variables in a `.env` file
-**winter:version** | the CLI command that detects the version of Winter CMS that is installed
+`/combine` | the asset combiner generator URL
+`/backend/system/updates` | the site updates context
+`/backend/system/install` | the installer path
+`/backend/backend/auth` | the backend authentication path (login, logout)
+`winter:up` | the CLI command that runs all pending migrations
+`winter:update` | the CLI command that triggers the update process
+`winter:env` | the CLI command that converts configuration files to environment variables in a `.env` file
+`winter:version` | the CLI command that detects the version of Winter CMS that is installed
Define the `$elevated` property to grant elevated permissions for your plugin.
diff --git a/plugin-scheduling.md b/plugin-scheduling.md
index ed6499ce..122d3b8c 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:
+In addition to scheduling `Closure` calls, you may also schedule [console commands](../console/introduction) 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,34 +92,42 @@ 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.
+In this example, the `emails:send` [console command](../console/introduction) 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.
## Task output
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 52cae47e..539dbf77 100644
--- a/plugin-settings.md
+++ b/plugin-settings.md
@@ -21,32 +21,36 @@ 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;
+```php
+// Outputs: ABCD
+echo Settings::instance()->api_key;
- // 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);
+// 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
@@ -95,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.
@@ -137,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).
@@ -154,28 +167,38 @@ 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 different environment. Inside the overridden configuration file you can return only values you want to override:
+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`).
- **NOTE:** In order for the config override to work, the plugin must contain a default config file (i.e. `plugins/author/plugin/config/config.php`. Even if you expect all configuration to come from the project override instead of the default configuration file it is still **highly** recommended that a default configuration file is provided as a form of documentation as to what configuration options are available to be modified on the project level.
- return [
- 'maxItems' => 20
- ];
+Inside the overridden configuration file you can return only values you want to override:
+
+```php
+ 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`.
@@ -183,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/plugin-updates.md b/plugin-updates.md
index 57823630..591db81c 100644
--- a/plugin-updates.md
+++ b/plugin-updates.md
@@ -16,13 +16,13 @@ The change log is stored in a YAML file called `version.yaml` inside the **/upda
```css
π plugins
- β£ π myauthor <-- Author name
- β β£ π myplugin <-- Plugin name
- β β β£ π updates <-- Database migrations
- β β β β β£ π v1.0.0 <-- Migrations for a specific version of the plugin
- β β β β β β£ π seed_the_database.php <-- Database seed file, referenced in version.yaml
- β β β β β β π create_records_table.php <-- Database migration file, referenced in version.yaml
- β β β β π version.yaml <-- Changelog
+ β π myauthor <-- Author name
+ β π myplugin <-- Plugin name
+ β π updates <-- Database migrations
+ β£ π v1.0.0 <-- Migrations for a specific version of the plugin
+ β β£ π seed_the_database.php <-- Database seed file, referenced in version.yaml
+ β β π create_records_table.php <-- Database migration file, referenced in version.yaml
+ β π version.yaml <-- Changelog
```
@@ -32,7 +32,7 @@ During an update the system will notify the user about recent changes to plugins
1. When an administrator signs in to the backend.
1. When the system is updated using the update feature in the backend area.
-1. When the [console command](../console/commands#console-up-command) `php artisan winter:up` is called in the command line from the application directory.
+1. When the [console command](../console/setup-maintenance#winter-up) `php artisan winter:up` is called in the command line from the application directory.
> **NOTE:** The plugin [initialization process](../plugin/registration#routing-initialization) is disabled during the update process, this should be a consideration in migration and seeding scripts.
diff --git a/services-application.md b/services-application.md
index 935ab917..6f51aa27 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):
+You may also use the `runningInConsole` method to check if the executing code is taking place inside the [command line interface](../console/introduction):
- App::runningInConsole();
+```php
+App::runningInConsole();
+```
diff --git a/services-asset-compilation.md b/services-asset-compilation.md
index 5fb4593b..4079d203 100644
--- a/services-asset-compilation.md
+++ b/services-asset-compilation.md
@@ -103,15 +103,23 @@ 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:
+
+
+
Alias | Description
------------- | -------------
`@jquery` | Reference to the jQuery library (v3.4.0) used in the backend. (JavaScript)
@@ -149,7 +157,7 @@ If you are wanting to render the injected assets in any other context, you can c
While the majority of the time dynamic asset compilation through `addJs()`, `addCss()`, or the [`| theme` filter](../markup/filter-theme) should be sufficient for your needs, you may occassionally have a complex asset compilation that you would like to just generate a static file on command instead of dynamically.
-The Winter CMS core registers several such bundles for internal usage that are compiled whenever the [`artisan winter:util compile assets` command](../console/commands#winter-util-command) is run.
+The Winter CMS core registers several such bundles for internal usage that are compiled whenever the [`artisan winter:util compile assets` command](../console/utilities#winter-util-compile-assets) is run.
## Extending the Asset Compiler
@@ -169,6 +177,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-behaviors.md b/services-behaviors.md
index 3f01f05a..76c395bd 100644
--- a/services-behaviors.md
+++ b/services-behaviors.md
@@ -245,11 +245,11 @@ This unique ability to extend constructors allows behaviors to be implemented dy
```php
/**
- * Extend the Winter.Users controller to include the RelationController behavior too
+ * Extend the Winter.Users Users controller to include the RelationController behavior too
*/
Winter\Users\Controllers\Users::extend(function($controller) {
// Implement the list controller behavior dynamically
- $controller->implement[] = 'Backend.Behaviors.RelationController';
+ $controller->implement[] = \Backend\Behaviors\RelationController::class;
// Declare the relationConfig property dynamically for the RelationController behavior to use
$controller->addDynamicProperty('relationConfig', '$/myvendor/myplugin/controllers/users/config_relation.yaml');
@@ -338,7 +338,7 @@ echo $controller->asExtension('FormController')->otherMethod();
To check if an object has been extended with a behavior, you may use the `isClassExtendedWith` method on the object.
```php
-$controller->isClassExtendedWith('Backend.Behaviors.RelationController');
+$controller->isClassExtendedWith(\Backend\Behaviors\RelationController::class);
```
Below is an example of dynamically extending a `UsersController` of a third-party plugin utilizing this method to avoid preventing other plugins from also extending the afore-mentioned third-party plugin.
@@ -346,8 +346,8 @@ Below is an example of dynamically extending a `UsersController` of a third-part
```php
UsersController::extend(function($controller) {
// Implement behavior if not already implemented
- if (!$controller->isClassExtendedWith('Backend.Behaviors.RelationController')) {
- $controller->implement[] = 'Backend.Behaviors.RelationController';
+ if (!$controller->isClassExtendedWith(\Backend\Behaviors\RelationController::class)) {
+ $controller->implement[] = \Backend\Behaviors\RelationController::class;
}
// Define property if not already defined
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-config-writer.md b/services-config-writer.md
new file mode 100644
index 00000000..7d9a0a5c
--- /dev/null
+++ b/services-config-writer.md
@@ -0,0 +1,300 @@
+# Config Writer
+
+- [Creating & updating a PHP config file](#create-and-update-php)
+ - [Setting properties](#setting-properties)
+ - [Multidimensional arrays](#multidimensional-arrays)
+ - [Setting env defaults](#setting-env-defaults)
+ - [Setting function calls](#setting-function-calls)
+ - [Setting constants](#setting-const)
+ - [Returning rendered PHP](#returning-rendered-php)
+ - [Config sorting](#config-sorting)
+ - [Writing to a different file than read](#writing-to-a-different-file)
+- [Creating & updating a .env file](#create-and-update-env)
+ - [Setting properties](#setting-properties-env)
+ - [Writing to a different file than read](#writing-to-a-different-file-env)
+ - [Adding new lines](#adding-new-lines)
+
+> Notice, the `ConfigWriter` class has been deprecated and replaced by `ConfigFile`
+
+
+## Creating & updating a PHP config file
+
+The `ConfigFile` class can be used to update & create a php config file. The `ConfigFile::read()` method will take the
+path of an existing file or create the file upon save if missing.
+
+```php
+use Winter\Storm\Config\ConfigFile;
+
+$config = ConfigFile::read('/path/to/file.php');
+$config->set('foo', 'bar');
+$config->write();
+```
+
+
+### Setting properties
+
+Setting properties can be chained or multiple properties can be set by passing an array
+
+```php
+ConfigFile::read('/path/to/file.php')
+ ->set('foo', 'bar')
+ ->set('bar', 'foo')
+ ->write();
+
+// or
+
+ConfigFile::read('/path/to/file.php')->set([
+ 'foo' => 'bar',
+ 'bar' => 'foo'
+])->write();
+```
+
+
+### Multidimensional arrays
+
+Multidimensional arrays can be set via dot notation, or by passing an array.
+
+```php
+ConfigFile::read('/path/to/file.php')->set([
+ 'foo.bar.a' => 'bar',
+ 'foo.bar.b' => 'foo'
+])->write();
+
+// or
+
+ConfigFile::read('/path/to/file.php')->set([
+ 'foo' => [
+ 'bar' => [
+ 'a' => 'bar',
+ 'b' => 'foo'
+ ]
+ ]
+])->write();
+```
+
+Will output:
+
+```php
+ [
+ 'bar' => [
+ 'a' => 'bar',
+ 'b' => 'foo',
+ ]
+ ]
+];
+```
+
+
+### Setting env defaults
+
+If a config file has a `env()`, then by setting the property of that function will set the default argument.
+
+For example, the following config:
+
+```php
+ [
+ 'bar' => env('EXAMPLE_KEY'),
+ ]
+];
+```
+
+After setting the position of the env function call.
+
+```php
+ConfigFile::read('/path/to/file.php')->set([
+ 'foo.bar' => 'Winter CMS',
+])->write();
+```
+
+Will result in:
+
+```php
+ [
+ 'bar' => env('EXAMPLE_KEY', 'Winter CMS'),
+ ]
+];
+```
+
+
+### Setting function calls
+
+Function calls can be added to your config either via the `ConfigFunction` class or using the `function()` helper method
+on the `ConfigFile` object.
+
+```php
+use Winter\Storm\Config\ConfigFile;
+use Winter\Storm\Config\ConfigFunction;
+
+ConfigFile::read('/path/to/file.php')->set([
+ 'foo.bar' => new ConfigFunction('env', ['argument1', 'argument1']),
+])->write();
+
+// or
+
+$config = ConfigFile::read('/path/to/file.php');
+$config->set([
+ 'foo.bar' => $config->function('env', ['argument1', 'argument1']),
+]);
+$config->write();
+```
+
+
+### Setting const
+
+Constants can be added to your config either via the `ConfigConst` class or using the `const()` helper method
+on the `ConfigFile` object.
+
+```php
+use Winter\Storm\Config\ConfigFile;
+use Winter\Storm\Config\ConfigConst;
+
+ConfigFile::read('/path/to/file.php')->set([
+ 'foo.bar' => new ConfigConst('PHP_OS'),
+])->write();
+
+// or
+
+$config = ConfigFile::read('/path/to/file.php');
+$config->set([
+ 'foo.bar' => $config->const('\Path\To\Class::VALUE'),
+]);
+$config->write();
+```
+
+
+### Returning rendered PHP
+
+If you require the php config as a string instead of a file, the `render()` method can be used.
+
+```php
+$phpConfigString = ConfigFile::read('/path/to/file.php')->set([
+ 'foo.bar' => 'Winter CMS',
+])->render();
+```
+
+
+### Config sorting
+
+The `ConfigFile` object supports sorting your config file before rendering.
+
+```php
+$config = ConfigFile::read('/path/to/file.php');
+$config->set([
+ 'b' => 'is awesome'
+ 'a.b' => 'CMS',
+ 'a.a' => 'Winter',
+]);
+$config->sort(ConfigFile::SORT_ASC);
+$config->write();
+```
+
+Will write out:
+
+```php
+ [
+ 'a' => 'Winter',
+ 'b' => 'CMS',
+ ],
+ 'b' => 'is awesome',
+];
+```
+
+The sort method supports the following options:
+
+- `ConfigFile::SORT_ASC`
+- `ConfigFile::SORT_DESC`
+- a callable function
+
+By default, `sort()` will use `ConfigFile::SORT_ASC`.
+
+
+### Writing to a different file than read
+
+If required, you can read from an existing file, and write out to a different file.
+
+```php
+ConfigFile::read('/path/to/file.php')->set([
+ 'foo.bar' => 'Winter CMS',
+])->write('/path/to/another.file.php');
+```
+
+
+## Creating & updating a .env file
+
+By default, the env file read will be `base_path('.env')`, this can be changed if required by passing the path to
+the `read()` method.
+
+> NOTE: properties are set in order, sorting is not supported.
+
+```php
+use Winter\Storm\Config\EnvFile;
+
+$env = EnvFile::read();
+$env->set('FOO', 'bar');
+$env->write();
+```
+
+
+### Setting properties
+
+Similar to the `ConfigFile` object, properties can be set either one at a time or by passing an array.
+
+> NOTE: dot notation is not supported by `EnvFile`
+
+```php
+$env = EnvFile::read();
+$env->set('FOO', 'bar');
+$env->set('BAR', 'foo');
+$env->write();
+
+// or
+
+EnvFile::read()->set([
+ 'FOO' => 'bar'
+ 'BAR' => 'foo'
+])->write();
+```
+
+
+### Writing to a different file than read
+
+If required, you can read from an existing file, and write out to a different file.
+
+```php
+EnvFile::read()->set([
+ 'FOO' => 'bar',
+])->write('/path/to/.env.alternative');
+```
+
+
+### Adding new lines
+
+If required, you can add new lines into the env file.
+
+```php
+$env = EnvFile::read();
+$env->set('FOO', 'bar');
+$env->addNewLine();
+$env->set('BAR', 'foo');
+$env->write();
+```
+
+Will output:
+
+```dotenv
+FOO="bar"
+
+BAR="foo"
+```
\ No newline at end of file
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..b4c63f00 100644
--- a/services-helpers.md
+++ b/services-helpers.md
@@ -16,6 +16,7 @@ Winter includes a variety of "helper" PHP functions. Many of these functions are
### Arrays
+[Laravel `Arr::*()` Helpers](https://laravel.com/docs/6.x/helpers#available-methods)
[array_add](#method-array-add)
[array_divide](#method-array-divide)
[array_dot](#method-array-dot)
@@ -56,6 +57,7 @@ Winter includes a variety of "helper" PHP functions. Many of these functions are
### Strings
+[Laravel `Str::*()` Helpers](https://laravel.com/docs/6.x/helpers#available-methods)
[camel_case](#method-camel-case)
[class_basename](#method-class-basename)
[e](#method-e)
@@ -113,250 +115,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 +407,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 +423,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 +579,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 +771,181 @@ 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');
+```
+
+You can configure the asset URL host by setting the `ASSET_URL` variable in your `.env` file (or `asset_url` in your `config/app.php` file). This can be useful if you host your assets on an external service like Amazon S3 or another CDN:
+
+```php
+// ASSET_URL=http://example.com/assets
+
+$url = asset('img/photo.jpg'); // http://example.com/assets/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
- = Form::open(..) ?>
+```php
+// PHP
+= Form::open(..) ?>
- // 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:
- = Form::open(['url' => 'foo/bar']) ?>
- //
- = Form::close() ?>
+```php
+= Form::open(['url' => 'foo/bar']) ?>
+ //
+= Form::close() ?>
+```
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:
- = Form::token() ?>
+```php
+= Form::token() ?>
+```
#### 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:
- = Form::sessionKey() ?>
+```php
+= Form::sessionKey() ?>
+```
## 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:
- = Form::model($user, ['id' => 'userForm']) ?>
+```php
+= Form::model($user, ['id' => '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:
- = Form::value('name', 'John Travolta') ?>
+```php
+= Form::value('name', 'John Travolta') ?>
+```
> **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
- = Form::label('email', 'E-Mail Address') ?>
+```php
+= Form::label('email', 'E-Mail Address') ?>
+```
#### Specifying extra HTML attributes
- = Form::label('email', 'E-Mail Address', ['class' => 'awesome']) ?>
+```php
+= Form::label('email', 'E-Mail Address', ['class' => '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
- = Form::text('username') ?>
+```php
+= Form::text('username') ?>
+```
#### Specifying a default value
- = Form::text('email', 'emailaddress@example.com') ?>
+```php
+= Form::text('email', 'emailaddress@example.com') ?>
+```
> **NOTE:** The *hidden* and *textarea* methods have the same signature as the *text* method.
#### Generating a password input
- = Form::password('password') ?>
+```php
+= Form::password('password') ?>
+```
#### Generating other inputs
- = Form::email($name, $value = null, $attributes = []) ?>
- = Form::file($name, $attributes = []) ?>
+```php
+= Form::email($name, $value = null, $attributes = []) ?>
+= Form::file($name, $attributes = []) ?>
+```
## Checkboxes and radio buttons
#### Generating a checkbox or radio input
- = Form::checkbox('name', 'value') ?>
+```php
+= Form::checkbox('name', 'value') ?>
- = Form::radio('name', 'value') ?>
+= Form::radio('name', 'value') ?>
+```
#### Generating a checkbox or radio input that is checked
- = Form::checkbox('name', 'value', true) ?>
+```php
+= Form::checkbox('name', 'value', true) ?>
- = Form::radio('name', 'value', true) ?>
+= Form::radio('name', 'value', true) ?>
+```
## Number
#### Generating a number input
- = Form::number('name', 'value') ?>
+```php
+= Form::number('name', 'value') ?>
+```
## File input
#### Generating a file input
- = Form::file('image') ?>
+```php
+= Form::file('image') ?>
+```
> **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
- = Form::select('size', ['L' => 'Large', 'S' => 'Small']) ?>
+```php
+= Form::select('size', ['L' => 'Large', 'S' => 'Small']) ?>
+```
#### Generating a drop-down list with selected default
- = Form::select('size', ['L' => 'Large', 'S' => 'Small'], 'S') ?>
+```php
+= Form::select('size', ['L' => 'Large', 'S' => 'Small'], 'S') ?>
+```
#### Generating a grouped list
- = Form::select('animal', [
- 'Cats' => ['leopard' => 'Leopard'],
- 'Dogs' => ['spaniel' => 'Spaniel'],
- ]) ?>
+```php
+= Form::select('animal', [
+ 'Cats' => ['leopard' => 'Leopard'],
+ 'Dogs' => ['spaniel' => 'Spaniel'],
+]) ?>
+```
#### Generating a drop-down list with a range
- = Form::selectRange('number', 10, 20) ?>
+```php
+= Form::selectRange('number', 10, 20) ?>
+```
#### Generating a drop-down list with a range, selected value and blank option
- = Form::selectRange('number', 10, 20, 2, ['emptyOption' => 'Choose...']) ?>
+```php
+= Form::selectRange('number', 10, 20, 2, ['emptyOption' => 'Choose...']) ?>
+```
#### Generating a list with month names
- = Form::selectMonth('month') ?>
+```php
+= Form::selectMonth('month') ?>
+```
#### Generating a list with month names, selected value and blank option
- = Form::selectMonth('month', 2, ['emptyOption' => 'Choose month...']) ?>
+```php
+= Form::selectMonth('month', 2, ['emptyOption' => 'Choose month...']) ?>
+```
## Buttons
#### Generating a submit button
- = Form::submit('Click Me!') ?>
+```php
+= Form::submit('Click Me!') ?>
+```
> **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
- = Form::myField() ?>
+```php
+= Form::myField() ?>
+```
diff --git a/services-image-resizing.md b/services-image-resizing.md
index a9996cea..36aaf47b 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`
@@ -59,6 +67,12 @@ Key | Description | Default | Options
The `mode` option allows you to specify how the image should be resized. The available modes are as follows:
+
+
+
Mode | Description
--- | ---
`auto` | Automatically choose between `portrait` and `landscape` based on the image's orientation
diff --git a/services-mail.md b/services-mail.md
index 1e285f60..89f48d3a 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,30 +85,40 @@ 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.
-Array | multiple recipients where the array key is the address and the value is the name.
-Object | a single recipient object, where the *email* property is used for the address and the *name* is optionally used for the name.
-Collection | a collection of recipient objects, as above.
+`String` | a single recipient address, with no name defined.
+`Array` | multiple recipients where the array key is the address and the value is the name.
+`Object` | a single recipient object, where the *email* property is used for the address and the *name* is optionally used for the name.
+`Collection` | a collection of recipient objects, as above.
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..48ecdf0b 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..a2f94c00 100644
--- a/services-queues.md
+++ b/services-queues.md
@@ -171,11 +171,11 @@ When using Iron.io [push queues](#push-queues), you should take extra precaution
## Running the queue worker
-Winter includes some [console commands](../console/commands) that will process jobs in the queue.
+Winter includes some [console commands](../console/introduction) that will process jobs in the queue.
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..86611117 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:
-
+```php
+
-
-
-
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.
+> **NOTE:** If using [Composer](../help/using-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..34b6c1cd 100644
--- a/setup-installation.md
+++ b/setup-installation.md
@@ -14,21 +14,21 @@
Documentation on the different ways to install Winter CMS for your next project.
-There are two ways you can install Winter, either using the [Web-based installer](#web-based-installation) or [Command-line installation](../console/commands#console-install) instructions. Before you proceed, you should check that your server meets the minimum system requirements.
+There are two ways you can install Winter, either using the [Web-based installer](#web-based-installation) or [Composer installation](../help/using-composer) instructions. Before you proceed, you should check that your server meets the minimum system requirements.
## Minimum system requirements
Winter CMS has some server requirements for web hosting:
-1. PHP version 7.2 or higher
-1. PDO PHP Extension (and relevant driver for the database you want to connect to)
-1. cURL PHP Extension
-1. OpenSSL PHP Extension
-1. Mbstring PHP Extension
-1. ZipArchive PHP Extension
-1. GD PHP Extension
-1. SimpleXML PHP Extension
+- PHP version 7.2 or higher
+- PDO PHP Extension (and relevant driver for the database you want to connect to)
+- cURL PHP Extension
+- OpenSSL PHP Extension
+- Mbstring PHP Extension
+- ZipArchive PHP Extension
+- GD PHP Extension
+- SimpleXML PHP Extension
Some OS distributions may require you to manually install some of the required PHP extensions.
@@ -46,7 +46,7 @@ When using the SQL Server database engine, you will need to install the [group c
The [Web Installer](https://github.com/wintercms/web-installer) is the recommended way to install Winter for **non-technical users**. It is simpler than the command-line installation and doesn't require any special skills.
-> **NOTE:** If you are a developer, we recommend that you [install via Composer instead](../console/commands#console-install-composer)
+> **NOTE:** If you are a developer, we recommend that you [install via Composer instead](../help/using-composer)
1. Prepare an empty directory on the web server that will host your Winter CMS installation. It can be a main domain, sub-domain or subfolder.
2. [Download the "install.zip" file](https://github.com/wintercms/web-installer/releases/latest) from the latest release of the Winter CMS Web Installer into this folder.
@@ -55,10 +55,10 @@ 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.
- {.img-responsive .frame}
+ {.img-responsive .frame}
-### Troubleshooting installation
+### Troubleshooting a web-based installation
1. **Unable to connect to the Winter Marketplace API**: If your server has a firewall blocking requests to port 443, you will need to allow requests and responses for this port. Contact your system administrator to allow access to this port.
@@ -73,7 +73,7 @@ The [Web Installer](https://github.com/wintercms/web-installer) is the recommend
## Command-line installation
-If you feel more comfortable with a command-line or want to use composer, there is a CLI install process on the [Console interface page](../console/commands#console-install).
+If you feel more comfortable with a command-line or want to use Composer, there is a CLI install process on the [Using Composer page](../help/using-composer)
## Post-installation steps
@@ -85,13 +85,15 @@ 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
-Configuration files are stored in the **config** directory of the application. While each file contains descriptions for each setting, it is important to review the [common configuration options](../setup/configuration) available for your circumstances.
+Configuration files are stored in the `config` directory of the application. While each file contains descriptions for each setting, it is important to review the [common configuration options](../setup/configuration) available for your circumstances.
For example, in production environments you may want to enable [CSRF protection](../setup/configuration#csrf-protection). While in development environments, you may want to enable [bleeding edge updates](../setup/configuration#edge-updates).
@@ -100,17 +102,17 @@ While most configuration is optional, we strongly recommend disabling [debug mod
### Setting up the scheduler
-For *scheduled tasks* to operate correctly, you should add the following Cron entry to your server. Editing the crontab is commonly performed with the command `crontab -e`.
+For scheduled tasks to operate correctly, you should add the following Cron entry to your server. Editing the crontab is commonly performed with the command `crontab -e`.
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
-Be sure to replace **/path/to/artisan** with the absolute path to the *artisan* file in the root directory of Winter. This Cron will call the command scheduler every minute. Then Winter evaluates any scheduled tasks and runs the tasks that are due.
+Be sure to replace `/path/to/artisan` with the absolute path to the `artisan` file in the root directory of Winter. This cron will call the command scheduler every minute, in which Winter will evaluate any scheduled tasks and run the tasks that are due.
-> **NOTE**: If you are adding this to `/etc/cron.d` you'll need to specify a user immediately after `* * * * *`.
+> **NOTE**: If you are adding this to `/etc/cron.d`, you'll need to specify a user immediately after `* * * * *`.
### Setting up queue workers
-You may optionally set up an external queue for processing *queued jobs*, by default these will be handled asynchronously by the platform. This behavior can be changed by setting the `default` parameter in the `config/queue.php`.
+You may optionally set up an external queue for processing queued jobs. By default, these will be handled asynchronously by the platform. This behavior can be changed by setting the `default` parameter in the `config/queue.php`.
If you decide to use the `database` queue driver, it is a good idea to add a Crontab entry for the command `php artisan queue:work --once` to process the first available job in the queue.
diff --git a/snowboard-data-attributes.md b/snowboard-data-attributes.md
new file mode 100644
index 00000000..af623de3
--- /dev/null
+++ b/snowboard-data-attributes.md
@@ -0,0 +1,100 @@
+# AJAX Requests (Data Attributes API)
+
+- [Introduction](#introduction)
+- [Available Data Attributes](#available-attributes)
+- [Usage Examples](#usage-examples)
+
+
+## Introduction
+
+The Data Attributes API is the simpler way of embedding AJAX functionality in your themes and plugins, and removes the need to be experienced with JavaScript. While the [JavaScript API](../snowboard/request) has had numerous changes from the original [AJAX framework](../ajax/introduction), the Data Attributes API has remain largely unchanged, despite being powered by the new Snowboard framework under the hood.
+
+It can be loaded by adding the following tag into your CMS Theme's page or layout:
+
+```twig
+{% snowboard request attr %}
+```
+
+> **NOTE:** As per the [Migration Guide](../snowboard/migration-guide), arbitrary JavaScript is no longer allowed through the Data Attributes API. Thus, the `data-request-before-update`, `data-request-success`, `data-request-error` and `data-request-complete` attributes are no longer supported. Please use the [JavaScript API](../snowboard/request) if you require this functionality.
+
+
+## Available Data Attributes
+
+Triggering an AJAX request from a valid element is as simple as adding the `data-request` attribute to that element. This generally should be done on a button, link, or form. You can also customize the AJAX request using the following attributes:
+
+
+
+
+Attribute | Description
+--------- | -----------
+`data-request` | Specifies the AJAX handler name to target for the request.
+`data-request-confirm` | Specifies the confirmation message to present to the user before proceeding with the request. If the user cancels, the request is not sent.
+`data-request-redirect` | Specifies a URL to redirect the browser to, if a successful AJAX response is received.
+`data-request-url` | Specifies the URL to send the AJAX request to. By default, this will be the current URL.
+`data-request-update` | Specifies a list of partials and page elements (CSS selectors) to update on a successful AJAX response. The format is as follows: `partial: selector, partial: selector`. Usage of quotes is required in most cases: `'partial': 'selector'`. If the selector is prepended with an `@` symbol, the content received from the server will be appended to the element. If the selector is prepended with a `^` symbol, the content will be prepended. Otherwise, received content will replace the original content in the element.
+`data-request-data` | Specifies additional data to send with the request to the server. The format is as follows: `'var': 'value', 'var2': 'new value'`. You may also specify this same attribute on any parent elements of the triggering element, and this data will be merged with the parent data (with the triggering data taking preference). It will also be merged with any form data, if this request triggers within a form.
+`data-request-form` | Specifies the form that the AJAX request will include its data from. If this is unspecified, the closest form will be used, or if the element itself is a form, then this will be used.
+`data-request-flash` | Specifies if flash messages will be accepted from the response.
+`data-request-files` | Specifies if file data will be included in the request. This will allow any file inputs in the form to work.
+`data-browser-validate` | Specifies if the in-built browser validation will be triggered. If present, the request will be cancelled if the browser validation fails.
+`data-track-input` | Specifies if an input will trigger an AJAX request anytime the input changes. An optional number can be specified in this attribute, which represents the amount of milliseconds between any change and the AJAX request triggering.
+
+
+When the `data-request` attribute is specified for an element, the element triggers an AJAX request when a user interacts with it. Depending on the type of element, the request is triggered on the following events:
+
+Element | Event
+------------- | -------------
+**Forms** | when the form is submitted.
+**Links, buttons** | when the element is clicked.
+**Text, number, and password fields** | when the text is changed and only if the `data-track-input` attribute is presented.
+**Dropdowns, checkboxes, radios** | when the element is selected.
+
+
+## Usage examples
+
+Trigger the `onCalculate` handler when the form is submitted. Update the element with the identifier "result" with the **calcresult** partial:
+
+```html
+
+```
diff --git a/snowboard-extras.md b/snowboard-extras.md
new file mode 100644
index 00000000..e120bf21
--- /dev/null
+++ b/snowboard-extras.md
@@ -0,0 +1,203 @@
+# Extra UI Features
+
+- [Introduction](#introduction)
+- [Loading indicator](#loader-stripe)
+- [Loading button](#loader-button)
+- [Flash messages](#ajax-flash)
+- [Form validation](#ajax-validation)
+ - [Throwing a validation error](#throw-validation-exception)
+ - [Displaying error messages](#error-messages)
+ - [Displaying errors with fields](#field-errors)
+ - [Usage examples](#usage-examples)
+
+
+## Introduction
+
+When using the Snowboard framework, you have the option to specify the `extras` flag which includes additional UI features. These features are often useful when working with AJAX requests in frontend CMS pages.
+
+```twig
+{% snowboard extras %}
+```
+
+
+## Loading indicator
+
+The loading indicator is a loading bar that is displayed on the top of the page when an AJAX request runs. The indicator hooks in to [global events](../snowboard/request#global-events) used by the Snowboard framework.
+
+When an AJAX request starts, the `ajaxPromise` event is fired. This displays the loading indicator at the top of the page. When this promise is resolved, the loading bar is removed.
+
+
+## Loading button
+
+When any element contains the `data-attach-loading` attribute, the CSS class `wn-loading` will be added to it during the AJAX request. This class will spawn a *loading spinner* on button and anchor elements using the `:after` CSS selector.
+
+```html
+
+
+
+ Do something
+
+```
+
+
+## Flash messages
+
+Specify the `data-request-flash` attribute on a form to enable the use of flash messages on successful AJAX requests.
+
+```html
+
+```
+
+Combined with use of the `Flash` facade in the event handler, a flash message will appear after the request finishes.
+
+```php
+function onSuccess()
+{
+ Flash::success('You did it!');
+}
+```
+
+When using AJAX Flash messages you should also ensure that your theme supports [standard flash messages](../markup/tag-flash) by placing the following code in your page or layout in order to render Flash messages that haven't been displayed yet when the page loads.
+
+```twig
+{% flash %}
+
+ {{ message }}
+
+{% endflash %}
+```
+
+
+## Form validation
+
+You may specify the `data-request-validate` attribute on a form to enable server-side validation features with fields and forms.
+
+```html
+
+```
+
+
+### Throwing a validation error
+
+In the server side AJAX handler, you may throw a [validation exception](../services/error-log#validation-exception) using the `ValidationException` class to make a field invalid. The exception should be provided an array, which states the field names for the keys, and the error messages for the values.
+
+```php
+function onSubmit()
+{
+ throw new ValidationException(['name' => 'You must give a name!']);
+}
+```
+
+> **NOTE**: You can also pass a [Validator](../services/validation) instance as the first argument of the exception instead, to use the in-built validation service.
+
+
+### Displaying error messages
+
+Inside the form, you may display the first error message by using the `data-validate-error` attribute on a container element. The content inside the container will be set to the error message and the element will be made visible.
+
+```html
+
+```
+
+To display multiple error messages, include an element with the `data-message` attribute. In this example the paragraph tag will be duplicated and set with content for each message that exists.
+
+```html
+
+```
+
+The `handleValidationErrors` callback, and the `ajaxValidationErrors` global event, that are available with the [Request API](../snowboard/request#global-events) allow you to fully customise the client-side validation handling. The `handleValidationErrors` callback can be used to control validation per request, while the `ajaxValidationErrors` global event can be used by [Snowboard plugins](../snowboard/plugin-development) to augment the client-side validation in a global fashion.
+
+
+### Displaying errors with fields
+
+Alternatively, you can show validation messages for individual fields by defining an element that uses the `data-validate-for` attribute, passing the field name as the value.
+
+```html
+
+
+
+
+
+```
+
+If the element is left empty, it will be populated with the validation text from the server. Otherwise you can specify any text you like and it will be displayed instead.
+
+```html
+
+ Oops.. phone number is invalid!
+
+```
+
+
+### Usage examples
+
+Below is a complete example of form validation. It calls the `onDoSomething` event handler that triggers a loading submit button, performs validation on the form fields, then displays a successful flash message.
+
+```html
+
+```
+
+The AJAX event handler looks at the POST data sent by the client and applies some rules to the validator. If the validation fails, a `ValidationException` is thrown, otherwise a `Flash::success` message is returned.
+
+```php
+function onDoSomething()
+{
+ $data = post();
+
+ $rules = [
+ 'name' => 'required',
+ 'email' => 'required|email',
+ ];
+
+ $validation = Validator::make($data, $rules);
+
+ if ($validation->fails()) {
+ throw new ValidationException($validation);
+ }
+
+ Flash::success('Jobs done!');
+}
+```
diff --git a/snowboard-handlers.md b/snowboard-handlers.md
new file mode 100644
index 00000000..3c67f8ad
--- /dev/null
+++ b/snowboard-handlers.md
@@ -0,0 +1,156 @@
+# Server-side Event Handlers
+
+- [Introduction](#introduction)
+ - [Calling a handler](#calling-handlers)
+ - [Generic handler](#generic-handler)
+- [Redirects in AJAX handlers](#redirects-in-handlers)
+- [Returning data from AJAX handlers](#returning-data-from-handlers)
+- [Throwing an AJAX exception](#throw-ajax-exception)
+- [Running code before handlers](#before-handler)
+
+
+## Introduction
+
+AJAX event handlers are PHP functions that can be defined in the page or layout [PHP section](../cms/themes#php-section) or inside [components](../cms/components) and are used to execute the server-side functionality of an AJAX request made by the [Request API](../snowboard/request) or [Data Attributes API](../snowboard/data-attributes).
+
+Handler method names should be specified with the `on` prefix, followed by the event name in PascalCase - for example, `onMyHandler` or `onCreatePost`.
+
+All handlers support the use of [updating partials](#updating-partials) as part of the AJAX response. This behavior can also be controlled via the `update` option in the [Request API](../snowboard/request) or the `data-request-update` attribute in the [Data Attributes API](../snowboard/data-attributes).
+
+```php
+function onSubmitContactForm()
+{
+ // AJAX handler functionality goes here
+}
+```
+
+If two handlers with the same name are defined in a page and layout together, the page handler will be executed. The handlers defined in [components](../cms/components) have the lowest priority.
+
+
+### Calling a handler
+
+Every AJAX request should specify a handler name. When the request is made, the server will search all the registered handlers and run the handler with the highest priority.
+
+```html
+
+
Go
+
+
+
+```
+
+If two components register the same handler name, it is advised to prefix the handler with the [component short name or alias](../cms/components#aliases). If a component uses an alias of **mycomponent** the handler can be targeted with `mycomponent::onName`.
+
+```html
+
Go
+```
+
+You should use the [`__SELF__`](../plugin/components#referencing-self) variable instead of the hard coded alias in order to support multiple instances of your component existing on the same page.
+
+```twig
+