diff --git a/artisan.md b/artisan.md
index 14fe0f4d9d9..9922a443dd5 100644
--- a/artisan.md
+++ b/artisan.md
@@ -24,7 +24,7 @@
## Introduction
-Artisan is the command-line interface included with Laravel. It provides a number of helpful commands that can assist you while you build your application. To view a list of all available Artisan commands, you may use the `list` command:
+Artisan is the command line interface included with Laravel. Artisan exists at the root of your application as the `artisan` script and provides a number of helpful commands that can assist you while you build your application. To view a list of all available Artisan commands, you may use the `list` command:
php artisan list
@@ -32,6 +32,13 @@ Every command also includes a "help" screen which displays and describes the com
php artisan help migrate
+
+#### Laravel Sail
+
+If you are using [Laravel Sail](/docs/{{version}}/sail) as your local development environment, remember to use the `sail` command line to invoke Artisan commands. Sail will execute your Artisan commands within your application's Docker containers:
+
+ ./sail artisan list
+
### Tinker (REPL)
@@ -40,7 +47,7 @@ Laravel Tinker is a powerful REPL for the Laravel framework, powered by the [Psy
#### Installation
-All Laravel applications include Tinker by default. However, you may install it manually if needed using Composer:
+All Laravel applications include Tinker by default. However, you may install Tinker using Composer if you have previously removed it from your application:
composer require laravel/tinker
@@ -49,7 +56,7 @@ All Laravel applications include Tinker by default. However, you may install it
#### Usage
-Tinker allows you to interact with your entire Laravel application on the command line, including the Eloquent ORM, jobs, events, and more. To enter the Tinker environment, run the `tinker` Artisan command:
+Tinker allows you to interact with your entire Laravel application on the command line, including your Eloquent models, jobs, events, and more. To enter the Tinker environment, run the `tinker` Artisan command:
php artisan tinker
@@ -59,10 +66,10 @@ You can publish Tinker's configuration file using the `vendor:publish` command:
> {note} The `dispatch` helper function and `dispatch` method on the `Dispatchable` class depends on garbage collection to place the job on the queue. Therefore, when using tinker, you should use `Bus::dispatch` or `Queue::push` to dispatch jobs.
-
-#### Command Whitelist
+
+#### Command Allow List
-Tinker utilizes a white-list to determine which Artisan commands are allowed to be run within its shell. By default, you may run the `clear-compiled`, `down`, `env`, `inspire`, `migrate`, `optimize`, and `up` commands. If you would like to white-list more commands you may add them to the `commands` array in your `tinker.php` configuration file:
+Tinker utilizes an "allow" list to determine which Artisan commands are allowed to be run within its shell. By default, you may run the `clear-compiled`, `down`, `env`, `inspire`, `migrate`, `optimize`, and `up` commands. If you would like to allow more commands you may add them to the `commands` array in your `tinker.php` configuration file:
'commands' => [
// App\Console\Commands\ExampleCommand::class,
@@ -71,7 +78,7 @@ Tinker utilizes a white-list to determine which Artisan commands are allowed to
#### Classes That Should Not Be Aliased
-Typically, Tinker automatically aliases classes as you require them in Tinker. However, you may wish to never alias some classes. You may accomplish this by listing the classes in the `dont_alias` array of your `tinker.php` configuration file:
+Typically, Tinker automatically aliases classes as you interact with them in Tinker. However, you may wish to never alias some classes. You may accomplish this by listing the classes in the `dont_alias` array of your `tinker.php` configuration file:
'dont_alias' => [
App\Models\User::class,
@@ -80,23 +87,21 @@ Typically, Tinker automatically aliases classes as you require them in Tinker. H
## Writing Commands
-In addition to the commands provided with Artisan, you may also build your own custom commands. Commands are typically stored in the `app/Console/Commands` directory; however, you are free to choose your own storage location as long as your commands can be loaded by Composer.
+In addition to the commands provided with Artisan, you may build your own custom commands. Commands are typically stored in the `app/Console/Commands` directory; however, you are free to choose your own storage location as long as your commands can be loaded by Composer.
### Generating Commands
-To create a new command, use the `make:command` Artisan command. This command will create a new command class in the `app/Console/Commands` directory. Don't worry if this directory does not exist in your application, since it will be created the first time you run the `make:command` Artisan command. The generated command will include the default set of properties and methods that are present on all commands:
+To create a new command, you may use the `make:command` Artisan command. This command will create a new command class in the `app/Console/Commands` directory. Don't worry if this directory does not exist in your application - it will be created the first time you run the `make:command` Artisan command:
php artisan make:command SendEmails
### Command Structure
-After generating your command, you should fill in the `signature` and `description` properties of the class, which will be used when displaying your command on the `list` screen. The `handle` method will be called when your command is executed. You may place your command logic in this method.
-
-> {tip} For greater code reuse, it is good practice to keep your console commands light and let them defer to application services to accomplish their tasks. In the example below, note that we inject a service class to do the "heavy lifting" of sending the e-mails.
+After generating your command, you should define appropriate values for the `signature` and `description` properties of the class. These properties will be used when displaying your command on the `list` screen. The `signature` property also allows you to define [your command's input expectations](#defining-input-expectations). The `handle` method will be called when your command is executed. You may place your command logic in this method.
-Let's take a look at an example command. Note that we are able to inject any dependencies we need into the command's `handle` method. The Laravel [service container](/docs/{{version}}/container) will automatically inject all dependencies that are type-hinted in this method's signature:
+Let's take a look at an example command. Note that we are able to request any dependencies we need via the command's `handle` method. The Laravel [service container](/docs/{{version}}/container) will automatically inject all dependencies that are type-hinted in this method's signature:
{tip} For greater code reuse, it is good practice to keep your console commands light and let them defer to application services to accomplish their tasks. In the example above, note that we inject a service class to do the "heavy lifting" of sending the e-mails.
+
### Closure Commands
-Closure based commands provide an alternative to defining console commands as classes. In the same way that route Closures are an alternative to controllers, think of command Closures as an alternative to command classes. Within the `commands` method of your `app/Console/Kernel.php` file, Laravel loads the `routes/console.php` file:
+Closure based commands provide an alternative to defining console commands as classes. In the same way that route closures are an alternative to controllers, think of command closures as an alternative to command classes. Within the `commands` method of your `app/Console/Kernel.php` file, Laravel loads the `routes/console.php` file:
/**
- * Register the Closure based commands for the application.
+ * Register the closure based commands for the application.
*
* @return void
*/
@@ -159,34 +166,34 @@ Closure based commands provide an alternative to defining console commands as cl
require base_path('routes/console.php');
}
-Even though this file does not define HTTP routes, it defines console based entry points (routes) into your application. Within this file, you may define all of your Closure based routes using the `Artisan::command` method. The `command` method accepts two arguments: the [command signature](#defining-input-expectations) and a Closure which receives the commands arguments and options:
+Even though this file does not define HTTP routes, it defines console based entry points (routes) into your application. Within this file, you may define all of your closure based console commands using the `Artisan::command` method. The `command` method accepts two arguments: the [command signature](#defining-input-expectations) and a closure which receives the commands arguments and options:
- Artisan::command('build {project}', function ($project) {
- $this->info("Building {$project}!");
+ Artisan::command('mail:send {user}', function ($user) {
+ $this->info("Sending email to: {$user}!");
});
-The Closure is bound to the underlying command instance, so you have full access to all of the helper methods you would typically be able to access on a full command class.
+The closure is bound to the underlying command instance, so you have full access to all of the helper methods you would typically be able to access on a full command class.
#### Type-Hinting Dependencies
-In addition to receiving your command's arguments and options, command Closures may also type-hint additional dependencies that you would like resolved out of the [service container](/docs/{{version}}/container):
+In addition to receiving your command's arguments and options, command closures may also type-hint additional dependencies that you would like resolved out of the [service container](/docs/{{version}}/container):
use App\Models\User;
use App\Support\DripEmailer;
- Artisan::command('email:send {user}', function (DripEmailer $drip, $user) {
+ Artisan::command('mail:send {user}', function (DripEmailer $drip, $user) {
$drip->send(User::find($user));
});
#### Closure Command Descriptions
-When defining a Closure based command, you may use the `describe` method to add a description to the command. This description will be displayed when you run the `php artisan list` or `php artisan help` commands:
+When defining a closure based command, you may use the `purpose` method to add a description to the command. This description will be displayed when you run the `php artisan list` or `php artisan help` commands:
- Artisan::command('build {project}', function ($project) {
- $this->info("Building {$project}!");
- })->describe('Build the project');
+ Artisan::command('mail:send {user}', function ($user) {
+ // ...
+ })->purpose('Send a marketing email to a user');
## Defining Input Expectations
@@ -196,94 +203,97 @@ When writing console commands, it is common to gather input from the user throug
### Arguments
-All user supplied arguments and options are wrapped in curly braces. In the following example, the command defines one **required** argument: `user`:
+All user supplied arguments and options are wrapped in curly braces. In the following example, the command defines one required argument: `user`:
/**
* The name and signature of the console command.
*
* @var string
*/
- protected $signature = 'email:send {user}';
+ protected $signature = 'mail:send {user}';
-You may also make arguments optional and define default values for arguments:
+You may also make arguments optional or define default values for arguments:
// Optional argument...
- email:send {user?}
+ mail:send {user?}
// Optional argument with default value...
- email:send {user=foo}
+ mail:send {user=foo}
### Options
-Options, like arguments, are another form of user input. Options are prefixed by two hyphens (`--`) when they are specified on the command line. There are two types of options: those that receive a value and those that don't. Options that don't receive a value serve as a boolean "switch". Let's take a look at an example of this type of option:
+Options, like arguments, are another form of user input. Options are prefixed by two hyphens (`--`) when they are provided via the command line. There are two types of options: those that receive a value and those that don't. Options that don't receive a value serve as a boolean "switch". Let's take a look at an example of this type of option:
/**
* The name and signature of the console command.
*
* @var string
*/
- protected $signature = 'email:send {user} {--queue}';
+ protected $signature = 'mail:send {user} {--queue}';
In this example, the `--queue` switch may be specified when calling the Artisan command. If the `--queue` switch is passed, the value of the option will be `true`. Otherwise, the value will be `false`:
- php artisan email:send 1 --queue
+ php artisan mail:send 1 --queue
#### Options With Values
-Next, let's take a look at an option that expects a value. If the user must specify a value for an option, suffix the option name with a `=` sign:
+Next, let's take a look at an option that expects a value. If the user must specify a value for an option, you should suffix the option name with a `=` sign:
/**
* The name and signature of the console command.
*
* @var string
*/
- protected $signature = 'email:send {user} {--queue=}';
+ protected $signature = 'mail:send {user} {--queue=}';
-In this example, the user may pass a value for the option like so:
+In this example, the user may pass a value for the option like so. If the option is not specified when invoking the command, its value will be `null`:
- php artisan email:send 1 --queue=default
+ php artisan mail:send 1 --queue=default
You may assign default values to options by specifying the default value after the option name. If no option value is passed by the user, the default value will be used:
- email:send {user} {--queue=default}
+ mail:send {user} {--queue=default}
#### Option Shortcuts
-To assign a shortcut when defining an option, you may specify it before the option name and use a | delimiter to separate the shortcut from the full option name:
+To assign a shortcut when defining an option, you may specify it before the option name and use the `|` character as a delimiter to separate the shortcut from the full option name:
- email:send {user} {--Q|queue}
+ mail:send {user} {--Q|queue}
### Input Arrays
-If you would like to define arguments or options to expect array inputs, you may use the `*` character. First, let's take a look at an example that specifies an array argument:
+If you would like to define arguments or options to expect multiple input values, you may use the `*` character. First, let's take a look at an example that specifies such an argument:
+
+ mail:send {user*}
- email:send {user*}
+When calling this method, the `user` arguments may be passed in order to the command line. For example, the following command will set the value of `user` to an array with `foo` and `bar` as its values:
-When calling this method, the `user` arguments may be passed in order to the command line. For example, the following command will set the value of `user` to `['foo', 'bar']`:
+ php artisan mail:send foo bar
- php artisan email:send foo bar
+
+#### Option Arrays
-When defining an option that expects an array input, each option value passed to the command should be prefixed with the option name:
+When defining an option that expects multiple input values, each option value passed to the command should be prefixed with the option name:
- email:send {user} {--id=*}
+ mail:send {user} {--id=*}
- php artisan email:send --id=1 --id=2
+ php artisan mail:send --id=1 --id=2
### Input Descriptions
-You may assign descriptions to input arguments and options by separating the parameter from the description using a colon. If you need a little extra room to define your command, feel free to spread the definition across multiple lines:
+You may assign descriptions to input arguments and options by separating the argument name from the description using a colon. If you need a little extra room to define your command, feel free to spread the definition across multiple lines:
/**
* The name and signature of the console command.
*
* @var string
*/
- protected $signature = 'email:send
+ protected $signature = 'mail:send
{user : The ID of the user}
{--queue= : Whether the job should be queued}';
@@ -293,12 +303,12 @@ You may assign descriptions to input arguments and options by separating the par
### Retrieving Input
-While your command is executing, you will obviously need to access the values for the arguments and options accepted by your command. To do so, you may use the `argument` and `option` methods:
+While your command is executing, you will likely need to access the values for the arguments and options accepted by your command. To do so, you may use the `argument` and `option` methods. If an argument or option does not exist, `null` will be returned:
/**
* Execute the console command.
*
- * @return mixed
+ * @return int
*/
public function handle()
{
@@ -316,11 +326,9 @@ Options may be retrieved just as easily as arguments using the `option` method.
// Retrieve a specific option...
$queueName = $this->option('queue');
- // Retrieve all options...
+ // Retrieve all options as an array...
$options = $this->options();
-If the argument or option does not exist, `null` will be returned.
-
### Prompting For Input
@@ -336,38 +344,48 @@ In addition to displaying output, you may also ask the user to provide input dur
$name = $this->ask('What is your name?');
}
-The `secret` method is similar to `ask`, but the user's input will not be visible to them as they type in the console. This method is useful when asking for sensitive information such as a password:
+The `secret` method is similar to `ask`, but the user's input will not be visible to them as they type in the console. This method is useful when asking for sensitive information such as passwords:
$password = $this->secret('What is the password?');
#### Asking For Confirmation
-If you need to ask the user for a simple confirmation, you may use the `confirm` method. By default, this method will return `false`. However, if the user enters `y` or `yes` in response to the prompt, the method will return `true`.
+If you need to ask the user for a simple "yes or no" confirmation, you may use the `confirm` method. By default, this method will return `false`. However, if the user enters `y` or `yes` in response to the prompt, the method will return `true`.
if ($this->confirm('Do you wish to continue?')) {
//
}
+If necessary, you may specify that the confirmation prompt should return `true` by default by passing `true` as the second argument to the `confirm` method:
+
+ if ($this->confirm('Do you wish to continue?', true)) {
+ //
+ }
+
#### Auto-Completion
-The `anticipate` method can be used to provide auto-completion for possible choices. The user can still choose any answer, regardless of the auto-completion hints:
+The `anticipate` method can be used to provide auto-completion for possible choices. The user can still provide any answer, regardless of the auto-completion hints:
$name = $this->anticipate('What is your name?', ['Taylor', 'Dayle']);
-Alternatively, you may pass a Closure as the second argument to the `anticipate` method. The Closure will be called each time the user types an input character. The Closure should accept a string parameter containing the user's input so far, and return an array of options for auto-completion:
+Alternatively, you may pass a closure as the second argument to the `anticipate` method. The closure will be called each time the user types an input character. The closure should accept a string parameter containing the user's input so far, and return an array of options for auto-completion:
- $name = $this->anticipate('What is your name?', function ($input) {
+ $name = $this->anticipate('What is your address?', function ($input) {
// Return auto-completion options...
});
#### Multiple Choice Questions
-If you need to give the user a predefined set of choices, you may use the `choice` method. You may set the array index of the default value to be returned if no option is chosen:
+If you need to give the user a predefined set of choices when asking a question, you may use the `choice` method. You may set the array index of the default value to be returned if no option is chosen by passing the index as the third argument to the method:
- $name = $this->choice('What is your name?', ['Taylor', 'Dayle'], $defaultIndex);
+ $name = $this->choice(
+ 'What is your name?',
+ ['Taylor', 'Dayle'],
+ $defaultIndex
+ );
In addition, the `choice` method accepts optional fourth and fifth arguments for determining the maximum number of attempts to select a valid response and whether multiple selections are permitted:
@@ -382,7 +400,7 @@ In addition, the `choice` method accepts optional fourth and fifth arguments for
### Writing Output
-To send output to the console, use the `line`, `info`, `comment`, `question` and `error` methods. Each of these methods will use appropriate ANSI colors for their purpose. For example, let's display some general information to the user. Typically, the `info` method will display in the console as green text:
+To send output to the console, you may use the `line`, `info`, `comment`, `question` and `error` methods. Each of these methods will use appropriate ANSI colors for their purpose. For example, let's display some general information to the user. Typically, the `info` method will display in the console as green colored text:
/**
* Execute the console command.
@@ -391,39 +409,52 @@ To send output to the console, use the `line`, `info`, `comment`, `question` and
*/
public function handle()
{
- $this->info('Display this on the screen');
+ // ...
+
+ $this->info('The command was successful!');
}
To display an error message, use the `error` method. Error message text is typically displayed in red:
$this->error('Something went wrong!');
-If you would like to display plain, uncolored console output, use the `line` method:
+You may use the `line` method to display plain, uncolored text:
$this->line('Display this on the screen');
You may use the `newLine` method to display a blank line:
+ // Write a single blank line...
$this->newLine();
// Write three blank lines...
$this->newLine(3);
-
-#### Table Layouts
-
-The `table` method makes it easy to correctly format multiple rows / columns of data. Just pass in the headers and rows to the method. The width and height will be dynamically calculated based on the given data:
+
+#### Tables
- $headers = ['Name', 'Email'];
+The `table` method makes it easy to correctly format multiple rows / columns of data. All you need to do is provide the column names and the data for the table and Laravel will
+automatically calculate the appropriate width and height of the table for you:
- $users = App\Models\User::all(['name', 'email'])->toArray();
+ use App\Models\User;
- $this->table($headers, $users);
+ $this->table(
+ ['Name', 'Email'],
+ User::all(['name', 'email'])->toArray()
+ );
#### Progress Bars
-For long running tasks, it could be helpful to show a progress indicator. Using the output object, we can start, advance and stop the Progress Bar. First, define the total number of steps the process will iterate through. Then, advance the Progress Bar after processing each item:
+For long running tasks, it can be helpful to show a progress bar that informs users how complete the task is. Using the `withProgressBar` method, Laravel will display a progress bar and advance its progress for each iteration over a given iterable value:
+
+ use App\Models\User;
+
+ $users = $this->withProgressBar(User::all(), function ($user) {
+ $this->performTask($user);
+ });
+
+Sometimes, you may need more manual control over how a progress bar is advanced. First, define the total number of steps the process will iterate through. Then, advance the progress bar after processing each item:
$users = App\Models\User::all();
@@ -439,12 +470,12 @@ For long running tasks, it could be helpful to show a progress indicator. Using
$bar->finish();
-For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/current/components/console/helpers/progressbar.html).
+> {tip} For more advanced options, check out the [Symfony Progress Bar component documentation](https://symfony.com/doc/current/components/console/helpers/progressbar.html).
## Registering Commands
-Because of the `load` method call in your console kernel's `commands` method, all commands within the `app/Console/Commands` directory will automatically be registered with Artisan. In fact, you are free to make additional calls to the `load` method to scan other directories for Artisan commands:
+All of your console commands are registered within your application's `App\Console\Kernel` class, which is your application's "console kernel". Within the `commands` method of this class, you will see a call to the kernel's `load` method. The `load` method will scan the `app/Console/Commands` directory and automatically register each command it contains with Artisan. You are even free to make additional calls to the `load` method to scan other directories for Artisan commands:
/**
* Register the commands for the application.
@@ -454,12 +485,12 @@ Because of the `load` method call in your console kernel's `commands` method, al
protected function commands()
{
$this->load(__DIR__.'/Commands');
- $this->load(__DIR__.'/MoreCommands');
+ $this->load(__DIR__.'/../Domain/Orders/Commands');
// ...
}
-You may also manually register commands by adding its class name to the `$commands` property of your `app/Console/Kernel.php` file. When Artisan boots, all the commands listed in this property will be resolved by the [service container](/docs/{{version}}/container) and registered with Artisan:
+If necessary, you may manually register commands by adding the command's class name to the `$commands` property of your `App\Console\Kernel` class. When Artisan boots, all the commands listed in this property will be resolved by the [service container](/docs/{{version}}/container) and registered with Artisan:
protected $commands = [
Commands\SendEmails::class
@@ -468,60 +499,69 @@ You may also manually register commands by adding its class name to the `$comman
## Programmatically Executing Commands
-Sometimes you may wish to execute an Artisan command outside of the CLI. For example, you may wish to fire an Artisan command from a route or controller. You may use the `call` method on the `Artisan` facade to accomplish this. The `call` method accepts either the command's name or class as the first argument, and an array of command parameters as the second argument. The exit code will be returned:
+Sometimes you may wish to execute an Artisan command outside of the CLI. For example, you may wish to execute an Artisan command from a route or controller. You may use the `call` method on the `Artisan` facade to accomplish this. The `call` method accepts either the command's signature name or class name as its first argument, and an array of command parameters as the second argument. The exit code will be returned:
- Route::get('/foo', function () {
- $exitCode = Artisan::call('email:send', [
- 'user' => 1, '--queue' => 'default'
- ]);
-
- //
- });
-
-Alternatively, you may pass the entire Artisan command to the `call` method as a string:
+ use Illuminate\Support\Facades\Artisan;
- Artisan::call('email:send 1 --queue=default');
-
-Using the `queue` method on the `Artisan` facade, you may even queue Artisan commands so they are processed in the background by your [queue workers](/docs/{{version}}/queues). Before using this method, make sure you have configured your queue and are running a queue listener:
-
- Route::get('/foo', function () {
- Artisan::queue('email:send', [
- 'user' => 1, '--queue' => 'default'
+ Route::post('/user/{user}/mail', function ($user) {
+ $exitCode = Artisan::call('mail:send', [
+ 'user' => $user, '--queue' => 'default'
]);
//
});
-You may also specify the connection or queue the Artisan command should be dispatched to:
+Alternatively, you may pass the entire Artisan command to the `call` method as a string:
- Artisan::queue('email:send', [
- 'user' => 1, '--queue' => 'default'
- ])->onConnection('redis')->onQueue('commands');
+ Artisan::call('mail:send 1 --queue=default');
#### Passing Array Values
If your command defines an option that accepts an array, you may pass an array of values to that option:
- Route::get('/foo', function () {
- $exitCode = Artisan::call('email:send', [
- 'user' => 1, '--id' => [5, 13]
+ use Illuminate\Support\Facades\Artisan;
+
+ Route::post('/mail', function () {
+ $exitCode = Artisan::call('mail:send', [
+ '--id' => [5, 13]
]);
});
#### Passing Boolean Values
-If you need to specify the value of an option that does not accept string values, such as the `--force` flag on the `migrate:refresh` command, you should pass `true` or `false`:
+If you need to specify the value of an option that does not accept string values, such as the `--force` flag on the `migrate:refresh` command, you should pass `true` or `false` as the value of the option:
$exitCode = Artisan::call('migrate:refresh', [
'--force' => true,
]);
+
+#### Queueing Artisan Commands
+
+Using the `queue` method on the `Artisan` facade, you may even queue Artisan commands so they are processed in the background by your [queue workers](/docs/{{version}}/queues). Before using this method, make sure you have configured your queue and are running a queue listener:
+
+ use Illuminate\Support\Facades\Artisan;
+
+ Route::post('/user/{user}/mail', function ($user) {
+ Artisan::queue('mail:send', [
+ 'user' => $user, '--queue' => 'default'
+ ]);
+
+ //
+ });
+
+Using the `onConnection` and `onQueue` methods, you may specify the connection or queue the Artisan command should be dispatched to:
+
+ Artisan::queue('mail:send', [
+ 'user' => 1, '--queue' => 'default'
+ ])->onConnection('redis')->onQueue('commands');
+
### Calling Commands From Other Commands
-Sometimes you may wish to call other commands from an existing Artisan command. You may do so using the `call` method. This `call` method accepts the command name and an array of command parameters:
+Sometimes you may wish to call other commands from an existing Artisan command. You may do so using the `call` method. This `call` method accepts the command name and an array of command arguments / options:
/**
* Execute the console command.
@@ -530,27 +570,27 @@ Sometimes you may wish to call other commands from an existing Artisan command.
*/
public function handle()
{
- $this->call('email:send', [
+ $this->call('mail:send', [
'user' => 1, '--queue' => 'default'
]);
//
}
-If you would like to call another console command and suppress all of its output, you may use the `callSilent` method. The `callSilent` method has the same signature as the `call` method:
+If you would like to call another console command and suppress all of its output, you may use the `callSilently` method. The `callSilently` method has the same signature as the `call` method:
- $this->callSilent('email:send', [
+ $this->callSilently('mail:send', [
'user' => 1, '--queue' => 'default'
]);
## Stub Customization
-The Artisan console's `make` commands are used to create a variety of classes, such as controllers, jobs, migrations, and tests. These classes are generated using "stub" files that are populated with values based on your input. However, you may sometimes wish to make small changes to files generated by Artisan. To accomplish this, you may use the `stub:publish` command to publish the most common stubs for customization:
+The Artisan console's `make` commands are used to create a variety of classes, such as controllers, jobs, migrations, and tests. These classes are generated using "stub" files that are populated with values based on your input. However, you may want to to make small changes to files generated by Artisan. To accomplish this, you may use the `stub:publish` command to publish the most common stubs to your application so that you can customize them:
php artisan stub:publish
-The published stubs will be located within a `stubs` directory in the root of your application. Any changes you make to these stubs will be reflected when you generate their corresponding classes using Artisan `make` commands.
+The published stubs will be located within a `stubs` directory in the root of your application. Any changes you make to these stubs will be reflected when you generate their corresponding classes using Artisan's `make` commands.
## Events
diff --git a/authentication.md b/authentication.md
index a9b369aca86..38980af113f 100644
--- a/authentication.md
+++ b/authentication.md
@@ -1,12 +1,11 @@
# Authentication
- [Introduction](#introduction)
+ - [Starter Kits](#starter-kits)
- [Database Considerations](#introduction-database-considerations)
- [Ecosystem Overview](#ecosystem-overview)
- [Authentication Quickstart](#authentication-quickstart)
- - [Routing](#included-routing)
- - [Views](#included-views)
- - [Authenticating](#included-authenticating)
+ - [Install A Starter Kit](#install-a-starter-kit)
- [Retrieving The Authenticated User](#retrieving-the-authenticated-user)
- [Protecting Routes](#protecting-routes)
- [Login Throttling](#login-throttling)
@@ -21,38 +20,40 @@
- [Configuration](#password-confirmation-configuration)
- [Routing](#password-confirmation-routing)
- [Protecting Routes](#password-confirmation-protecting-routes)
-- [Social Authentication](/docs/{{version}}/socialite)
- [Adding Custom Guards](#adding-custom-guards)
- [Closure Request Guards](#closure-request-guards)
- [Adding Custom User Providers](#adding-custom-user-providers)
- [The User Provider Contract](#the-user-provider-contract)
- [The Authenticatable Contract](#the-authenticatable-contract)
+- [Social Authentication](/docs/{{version}}/socialite)
- [Events](#events)
## Introduction
-Laravel makes implementing authentication very simple. In fact, almost everything is configured for you out of the box. The authentication configuration file is located at `config/auth.php`, which contains several well documented options for tweaking the behavior of the authentication services.
+Many web applications provide a way for their users to authenticate with the application and "login". Implementing this feature in web applications can be a complex and potentially risky endeavor. For this reason, Laravel strives to give you the tools you need to implement authentication quickly, securely, and easily.
At its core, Laravel's authentication facilities are made up of "guards" and "providers". Guards define how users are authenticated for each request. For example, Laravel ships with a `session` guard which maintains state using session storage and cookies.
-Providers define how users are retrieved from your persistent storage. Laravel ships with support for retrieving users using Eloquent and the database query builder. However, you are free to define additional providers as needed for your application.
+Providers define how users are retrieved from your persistent storage. Laravel ships with support for retrieving users using [Eloquent](/docs/{{version}}/eloquent) and the database query builder. However, you are free to define additional providers as needed for your application.
+
+Your application's authentication configuration file is located at `config/auth.php`. This file contains several well documented options for tweaking the behavior of Laravel's authentication services.
-Don't worry if this all sounds confusing now! Many applications will never need to modify the default authentication configuration.
+
+### Starter Kits
-
-#### Getting Started Fast
+Want to get started fast? Install a [Laravel application starter kit](/docs/{{version}}/starter-kits) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. The starter kits will take care of scaffolding your entire authentication system!
-Want to get started fast? Install [Laravel Jetstream](https://jetstream.laravel.com) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. Jetstream will take care of scaffolding your entire authentication system!
+**Even if you choose to not use a starter kit in your final Laravel application, installing the [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze) starter kit can be a wonderful opportunity to learn how to implement all of Laravel's authentication functionality in an actual Laravel project.** Since Laravel Breeze creates authentication controllers, routes, and views for you, you can examine the code within these files to learn how Laravel's authentication features may be implemented.
### Database Considerations
-By default, Laravel includes an `App\Models\User` [Eloquent model](/docs/{{version}}/eloquent) in your `app/Models` directory. This model may be used with the default Eloquent authentication driver. If your application is not using Eloquent, you may use the `database` authentication driver which uses the Laravel query builder.
+By default, Laravel includes an `App\Models\User` [Eloquent model](/docs/{{version}}/eloquent) in your `app/Models` directory. This model may be used with the default Eloquent authentication driver. If your application is not using Eloquent, you may use the `database` authentication provider which uses the Laravel query builder.
-When building the database schema for the `App\Models\User` model, make sure the password column is at least 60 characters in length. Maintaining the default string column length of 255 characters would be a good choice.
+When building the database schema for the `App\Models\User` model, make sure the password column is at least 60 characters in length. Of course, the `users` table migration that is included in new Laravel applications already creates a column that exceeds this length.
-Also, you should verify that your `users` (or equivalent) table contains a nullable, string `remember_token` column of 100 characters. This column will be used to store a token for users that select the "remember me" option when logging into your application.
+Also, you should verify that your `users` (or equivalent) table contains a nullable, string `remember_token` column of 100 characters. This column will be used to store a token for users that select the "remember me" option when logging into your application. Again, the default `users` table migration that is included in new Laravel applications already contains this column.
### Ecosystem Overview
@@ -61,18 +62,22 @@ Laravel offers several packages related to authentication. Before continuing, we
First, consider how authentication works. When using a web browser, a user will provide their username and password via a login form. If these credentials are correct, the application will store information about the authenticated user in the user's [session](/docs/{{version}}/session). A cookie issued to the browser contains the session ID so that subsequent requests to the application can associate the user with the correct session. After the session cookie is received, the application will retrieve the session data based on the session ID, note that the authentication information has been stored in the session, and will consider the user as "authenticated".
-When a remote service needs to authenticate to access an API, cookies are not typically used because there is no web browser. Instead, the remote service sends an API token to the API on each request. The application may validate the incoming token against a table of valid API tokens and "authenticate" the request as being performed by the user associated with that API token.
+When a remote service needs to authenticate to access an API, cookies are not typically used for authentication because there is no web browser. Instead, the remote service sends an API token to the API on each request. The application may validate the incoming token against a table of valid API tokens and "authenticate" the request as being performed by the user associated with that API token.
#### Laravel's Built-in Browser Authentication Services
-Laravel includes built-in authentication and session services which are typically accessed via the `Auth` and `Session` facades. These features provide cookie based authentication for requests that are initiated from web browsers. They provide methods that allow you to verify a user's credentials and authenticate the user. In addition, these services will automatically store the proper data in the user's session and issue the proper session cookie. A discussion of how to use these services is contained within this documentation.
+Laravel includes built-in authentication and session services which are typically accessed via the `Auth` and `Session` facades. These features provide cookie based authentication for requests that are initiated from web browsers. They provide methods that allow you to verify a user's credentials and authenticate the user. In addition, these services will automatically store the proper authentication data in the user's session and issue the user's session cookie. A discussion of how to use these services is contained within this documentation.
+
+**Application Starter Kits**
+
+As discussed in this documentation, you can interact with these authentication services manually to build your application's own authentication layer. However, to help you get started more quickly, we have released [free packages](/docs/{{version}}/starter-kits) that provide robust, modern scaffolding of the entire authentication layer. These packages are [Laravel Breeze](/docs/{{version}}/starter-kits#laravel-breeze), [Laravel Jetstream](/docs/{{version}}/starter-kits#laravel-jetstream), and [Laravel Fortify](https://github.com/laravel/fortify).
-**Jetstream / Fortify**
+_Laravel Breeze_ is a simple, minimal implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is comprised of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com). To get started, check out the documentation on Laravel's [application starter kits](/docs/{{version}}/starter-kits).
-As discussed in this documentation, you can interact with these authentication services manually to build your application's own authentication layer. However, to help you get started more quickly, we have released free packages that provide robust, modern scaffolding of the entire authentication layer. These packages are [Laravel Jetstream](https://jetstream.laravel.com) and [Laravel Fortify](https://github.com/laravel/fortify).
+_Laravel Fortify_ is a headless authentication backend for Laravel that implements many of the features found in this documentation, including cookie-based authentication as well as other features such as two-factor authentication and email verification. Fortify provides the authentication backend for Laravel Jetstream or may be used independently in combination with [Laravel Sanctum](/docs/{{version}}/sanctum) to provide authentication for an SPA that needs to authenticate with Laravel.
-Laravel Fortify is a headless authentication backend for Laravel that implements many of the features found in this documentation, including cookie-based authentication as well as other features such as two-factor authentication and email verification. Laravel Jetstream is a UI that consumes and exposes Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Laravel Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream, in addition to offering browser-based cookie authentication, includes built-in integration with Laravel Sanctum to offer API token authentication. Laravel's API authentication offerings are discussed below.
+_[Laravel Jetstream](https://jetstream.laravel.com)_ is a robust application starter kit that consumes and exposes Laravel Fortify's authentication services with a beautiful, modern UI powered by [Tailwind CSS](https://tailwindcss.com), [Livewire](https://laravel-livewire.com), and / or [Inertia.js](https://inertiajs.com). Laravel Jetstream includes optional support for two-factor authentication, team support, browser session management, profile management, and built-in integration with [Laravel Sanctum](/docs/{{version}}/sanctum) to offer API token authentication. Laravel's API authentication offerings are discussed below.
#### Laravel's API Authentication Services
@@ -85,18 +90,20 @@ Passport is an OAuth2 authentication provider, offering a variety of OAuth2 "gra
**Sanctum**
-In response to the complexity of OAuth2 and developer confusion, we set out to build a simpler, more streamlined authentication package that could handle both first-party web requests from a web browser and API requests via tokens. This goal was realized with the release of [Laravel Sanctum](/docs/{{version}}/sanctum), which should be considered the preferred and recommended authentication package for applications that will be offering a first-party web UI in addition to an API, or will be powered by a single-page application that exists separately from the backend Laravel application, or applications that offer a mobile client.
+In response to the complexity of OAuth2 and developer confusion, we set out to build a simpler, more streamlined authentication package that could handle both first-party web requests from a web browser and API requests via tokens. This goal was realized with the release of [Laravel Sanctum](/docs/{{version}}/sanctum), which should be considered the preferred and recommended authentication package for applications that will be offering a first-party web UI in addition to an API, or will be powered by a single-page application (SPA) that exists separately from the backend Laravel application, or applications that offer a mobile client.
Laravel Sanctum is a hybrid web / API authentication package that can manage your application's entire authentication process. This is possible because when Sanctum based applications receive a request, Sanctum will first determine if the request includes a session cookie that references an authenticated session. Sanctum accomplishes this by calling Laravel's built-in authentication services which we discussed earlier. If the request is not being authenticated via a session cookie, Sanctum will inspect the request for an API token. If an API token is present, Sanctum will authenticate the request using that token. To learn more about this process, please consult Sanctum's ["how it works"](/docs/{{version}}/sanctum#how-it-works) documentation.
-Laravel Sanctum is the API package we have chosen to include with the [Laravel Jetstream](https://jetstream.laravel.com) authentication scaffolding because we believe it is the best fit for the majority of web application's authentication needs.
+Laravel Sanctum is the API package we have chosen to include with the [Laravel Jetstream](https://jetstream.laravel.com) application starter kit because we believe it is the best fit for the majority of web application's authentication needs.
#### Summary & Choosing Your Stack
-In summary, if your application will be accessed using a browser, your application will use Laravel's built-in authentication services.
+In summary, if your application will be accessed using a browser and you are building a monolithic Laravel application, your application will use Laravel's built-in authentication services.
-Next, if your application offers an API, you will choose between [Passport](/docs/{{version}}/passport) or [Sanctum](/docs/{{version}}/sanctum) to provide API token authentication for your application. In general, Sanctum should be preferred when possible since it is a simple, complete solution for API authentication, SPA authentication, and mobile authentication, including support for "scopes" or "abilities".
+Next, if your application offers an API that will be consumed by third parties, you will choose between [Passport](/docs/{{version}}/passport) or [Sanctum](/docs/{{version}}/sanctum) to provide API token authentication for your application. In general, Sanctum should be preferred when possible since it is a simple, complete solution for API authentication, SPA authentication, and mobile authentication, including support for "scopes" or "abilities".
+
+If you are building a single-page application (SPA) that will be powered by a Laravel backend. You should use [Laravel Sanctum](/docs/{{version}}/sanctum). When using Sanctum, you will either need to [manually implement your own backend authentication routes](#authenticating-users) or utilize [Laravel Fortify](https://github.com/laravel/fortify) as a headless authentication backend provider.
Passport may be chosen when your application absolutely needs all of the features provided by the OAuth2 specification.
@@ -105,67 +112,31 @@ And, if you would like to get started quickly, we are pleased to recommend [Lara
## Authentication Quickstart
-> {note} This portion of the documentation discusses authenticating users via the [Laravel Jetstream](https://jetstream.laravel.com) package, which includes UI scaffolding to help you get started quickly. If you would like to integrate with Laravel's authentication systems directly, check out the documentation on [manually authenticating users](#authenticating-users).
-
-
-### Routing
-
-Laravel's `laravel/jetstream` package provides a quick way to scaffold all of the routes, views, and other backend logic needed for authentication using a few simple commands:
-
- composer require laravel/jetstream
-
- // Install Jetstream with the Livewire stack...
- php artisan jetstream:install livewire
-
- // Install Jetstream with the Inertia stack...
- php artisan jetstream:install inertia
-
-This command should be used on fresh applications and will install a layout view, registration and login views, as well as routes for all authentication end-points. A `/dashboard` route will also be generated to handle post-login requests to your application's dashboard.
-
-
-#### Creating Applications Including Authentication
-
-If you are starting a brand new application and would like to include the authentication scaffolding, you may use the `--jet` directive when creating your application via the Laravel Installer. This command will create a new application with all of the authentication scaffolding compiled and installed:
-
- laravel new kitetail --jet
-
-> {tip} To learn more about Jetstream, please visit the official [Jetstream documentation](https://jetstream.laravel.com).
+> {note} This portion of the documentation discusses authenticating users via the [Laravel application starter kits](/docs/{{version}}/starter-kits), which includes UI scaffolding to help you get started quickly. If you would like to integrate with Laravel's authentication systems directly, check out the documentation on [manually authenticating users](#authenticating-users).
-
-### Views
+
+### Install A Starter Kit
-As mentioned in the previous section, the `laravel/jetstream` package's `php artisan jetstream:install` command will create all of the views you need for authentication and place them in the `resources/views/auth` directory.
+First, you should [install a Laravel application starter kit](/docs/{{version}}/starter-kits). Our current starter kits, Laravel Breeze and Laravel Jetstream, offer beautifully designed starting points for incorporating authentication into your fresh Laravel application.
-Jetstream will also create a `resources/views/layouts` directory containing a base layout for your application. All of these views use the [Tailwind CSS](https://tailwindcss.com) framework, but you are free to customize them however you wish.
+Laravel Breeze is a minimal, simple implementation of all of Laravel's authentication features, including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](htts://tailwindcss.com).
-
-### Authenticating
-
-Now that your application has been scaffolded for authentication, you are ready to register and authenticate! You may simply access your application in a browser since Jetstream's authentication controllers already contain the logic to authenticate existing users and store new users in the database.
-
-
-#### Path Customization
-
-When a user is successfully authenticated, they will typically be redirected to the `/home` URI. You can customize the post-authentication redirect path using the `HOME` constant defined in your `RouteServiceProvider`:
-
- public const HOME = '/home';
-
-When using Laravel Jetstream, the Jetstream installation process will change the value of the `HOME` constant to `/dashboard`.
+[Laravel Jetstream](https://jetstream.laravel.com) is a more robust application starter kit that includes support for scaffolding your application with [Livewire](https://laravel-livewire.com) or [Inertia.js and Vue](https://inertiajs.com). In addition, Jetstream features optional support for two-factor authentication, teams, profile management, browser session management, API support via [Laravel Sanctum](/docs/{{version}}/sanctum), account deletion, and more.
### Retrieving The Authenticated User
-While handling an incoming request, you may access the authenticated user via the `Auth` facade:
+After installing an authentication starter kit and allowing users to register and authenticate with your application, you will often need to interact with the currently authenticated user. While handling an incoming request, you may access the authenticated user via the `Auth` facade's `user` method:
use Illuminate\Support\Facades\Auth;
- // Get the currently authenticated user...
+ // Retrieve the currently authenticated user...
$user = Auth::user();
- // Get the currently authenticated user's ID...
+ // Retrieve the currently authenticated user's ID...
$id = Auth::id();
-Alternatively, once a user is authenticated, you may access the authenticated user via an `Illuminate\Http\Request` instance. Remember, type-hinted classes will automatically be injected into your controller methods. By type-hinting the `Illuminate\Http\Request` object, you may gain convenient access to the authenticated user from any controller method in your application:
+Alternatively, once a user is authenticated, you may access the authenticated user via an `Illuminate\Http\Request` instance. Remember, type-hinted classes will automatically be injected into your controller methods. By type-hinting the `Illuminate\Http\Request` object, you may gain convenient access to the authenticated user from any controller method in your application via the request's `user` method:
user() returns an instance of the authenticated user...
+ // $request->user()
}
}
#### Determining If The Current User Is Authenticated
-To determine if the user is already logged into your application, you may use the `check` method on the `Auth` facade, which will return `true` if the user is authenticated:
+To determine if the user making the incoming HTTP request is authenticated, you may use the `check` method on the `Auth` facade. This method will return `true` if the user is authenticated:
use Illuminate\Support\Facades\Auth;
@@ -203,16 +174,16 @@ To determine if the user is already logged into your application, you may use th
### Protecting Routes
-[Route middleware](/docs/{{version}}/middleware) can be used to only allow authenticated users to access a given route. Laravel ships with an `auth` middleware, which references the `Illuminate\Auth\Middleware\Authenticate` class. Since this middleware is already registered in your HTTP kernel, all you need to do is attach the middleware to a route definition:
+[Route middleware](/docs/{{version}}/middleware) can be used to only allow authenticated users to access a given route. Laravel ships with an `auth` middleware, which references the `Illuminate\Auth\Middleware\Authenticate` class. Since this middleware is already registered in your application's HTTP kernel, all you need to do is attach the middleware to a route definition:
- Route::get('flights', function () {
- // Only authenticated users may enter...
+ Route::get('/flights', function () {
+ // Only authenticated users may access this route...
})->middleware('auth');
#### Redirecting Unauthenticated Users
-When the `auth` middleware detects an unauthorized user, it will redirect the user to the `login` [named route](/docs/{{version}}/routing#named-routes). You may modify this behavior by updating the `redirectTo` function in your `app/Http/Middleware/Authenticate.php` file:
+When the `auth` middleware detects an unauthenticated user, it will redirect the user to the `login` [named route](/docs/{{version}}/routing#named-routes). You may modify this behavior by updating the `redirectTo` function in your application's `app/Http/Middleware/Authenticate.php` file:
/**
* Get the path the user should be redirected to.
@@ -228,25 +199,25 @@ When the `auth` middleware detects an unauthorized user, it will redirect the us
#### Specifying A Guard
-When attaching the `auth` middleware to a route, you may also specify which guard should be used to authenticate the user. The guard specified should correspond to one of the keys in the `guards` array of your `auth.php` configuration file:
+When attaching the `auth` middleware to a route, you may also specify which "guard" should be used to authenticate the user. The guard specified should correspond to one of the keys in the `guards` array of your `auth.php` configuration file:
- Route::get('flights', function () {
- // Only authenticated users may enter...
- })->middleware('auth:api');
+ Route::get('/flights', function () {
+ // Only authenticated users may access this route...
+ })->middleware('auth:admin');
### Login Throttling
-If you are using Laravel Jetstream, rate limiting will automatically be applied to login attempts. By default, the user will not be able to login for one minute if they fail to provide the correct credentials after several attempts. The throttling is unique to the user's username / e-mail address and their IP address.
+If you are using the Laravel Breeze or Laravel Jetstream [starter kits](/docs/{{version}}/starter-kits), rate limiting will automatically be applied to login attempts. By default, the user will not be able to login for one minute if they fail to provide the correct credentials after several attempts. The throttling is unique to the user's username / email address and their IP address.
-> {tip} If you would like to rate limit your own routes, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting).
+> {tip} If you would like to rate limit other routes in your application, check out the [rate limiting documentation](/docs/{{version}}/routing#rate-limiting).
## Manually Authenticating Users
-You are not required to use the authentication scaffolding included with Laravel Jetstream. If you choose to not use this scaffolding, you will need to manage user authentication using the Laravel authentication classes directly. Don't worry, it's a cinch!
+You are not required to use the authentication scaffolding included with Laravel's [application starter kits](/docs/{{version}}/starter-kits). If you choose to not use this scaffolding, you will need to manage user authentication using the Laravel authentication classes directly. Don't worry, it's a cinch!
-We will access Laravel's authentication services via the `Auth` [facade](/docs/{{version}}/facades), so we'll need to make sure to import the `Auth` facade at the top of the class. Next, let's check out the `attempt` method:
+We will access Laravel's authentication services via the `Auth` [facade](/docs/{{version}}/facades), so we'll need to make sure to import the `Auth` facade at the top of the class. Next, let's check out the `attempt` method. The `attempt` method is normally used to handle authentication attempt's from your application's "login" form. If authentication is successful, you should regenerate the user's [session](/docs/{{version}}/session) to prevent [session fixation](https://en.wikipedia.org/wiki/Session_fixation):
only('email', 'password');
if (Auth::attempt($credentials)) {
- // Authentication passed...
+ $request->session()->regenerate();
+
return redirect()->intended('dashboard');
}
+
+ return back()->withErrors([
+ 'email' => 'The provided credentials do not match our records.',
+ ]);
}
}
-The `attempt` method accepts an array of key / value pairs as its first argument. The values in the array will be used to find the user in your database table. So, in the example above, the user will be retrieved by the value of the `email` column. If the user is found, the hashed password stored in the database will be compared with the `password` value passed to the method via the array. You should not hash the password specified as the `password` value, since the framework will automatically hash the value before comparing it to the hashed password in the database. If the two hashed passwords match an authenticated session will be started for the user.
+The `attempt` method accepts an array of key / value pairs as its first argument. The values in the array will be used to find the user in your database table. So, in the example above, the user will be retrieved by the value of the `email` column. If the user is found, the hashed password stored in the database will be compared with the `password` value passed to the method via the array. You should not hash the incoming request's `password` value, since the framework will automatically hash the value before comparing it to the hashed password in the database. If the two hashed passwords match an authenticated session will be started for the user.
+
+Remember, Laravel's authentication services will retrieve users from your database based on your authentication guard's "provider" configuration. In the default `config/auth.php` configuration file, the Eloquent user provider is specified and it is instructed to use the `App\Models\User` model when retrieving users. You may change these values within your configuration file based on the needs of your application.
The `attempt` method will return `true` if authentication was successful. Otherwise, `false` will be returned.
-The `intended` method on the redirector will redirect the user to the URL they were attempting to access before being intercepted by the authentication middleware. A fallback URI may be given to this method in case the intended destination is not available.
+The `intended` method provided by Laravel's redirector will redirect the user to the URL they were attempting to access before being intercepted by the authentication middleware. A fallback URI may be given to this method in case the intended destination is not available.
#### Specifying Additional Conditions
-If you wish, you may also add extra conditions to the authentication query in addition to the user's e-mail and password. For example, we may verify that the user is marked as "active":
+If you wish, you may also add extra query conditions to the authentication query in addition to the user's email and password. To accomplish this, we may simply add the query conditions to the array passed to the `attempt` method. For example, we may verify that the user is marked as "active":
if (Auth::attempt(['email' => $email, 'password' => $password, 'active' => 1])) {
- // The user is active, not suspended, and exists.
+ // Authentication was successful...
}
-> {note} In these examples, `email` is not a required option, it is merely used as an example. You should use whatever column name corresponds to a "username" in your database.
+> {note} In these examples, `email` is not a required option, it is merely used as an example. You should use whatever column name corresponds to a "username" in your database table.
#### Accessing Specific Guard Instances
-You may specify which guard instance you would like to utilize using the `guard` method on the `Auth` facade. This allows you to manage authentication for separate parts of your application using entirely separate authenticatable models or user tables.
+Via the `Auth` facade's `guard` method, you may specify which guard instance you would like to utilize when authenticating the user. This allows you to manage authentication for separate parts of your application using entirely separate authenticatable models or user tables.
The guard name passed to the `guard` method should correspond to one of the guards configured in your `auth.php` configuration file:
if (Auth::guard('admin')->attempt($credentials)) {
- //
+ // ...
}
#### Logging Out
-To log users out of your application, you may use the `logout` method on the `Auth` facade. This will clear the authentication information in the user's session:
+To log users out of your application, you may use the `logout` method on the `Auth` facade. This will clear the authentication information in the user's session so that subsequent requests to the application are not authenticated.
+
+In addition to calling the `logout` method, it is recommended that you invalidate the user's session and regenerate their [CSRF token](/docs/{{version}}/csrf). After logging the user out, you would typically redirect the user to the root of your application:
+
+ use Illuminate\Http\Request;
+ use Illuminate\Support\Facades\Auth;
- Auth::logout();
+ /**
+ * Log the user out of the application.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\Response
+ */
+ public function logout(Request $request)
+ {
+ Auth::logout();
+
+ $request->session()->invalidate();
+
+ $request->session()->regenerateToken();
+
+ return redirect('/');
+ }
### Remembering Users
-If you would like to provide "remember me" functionality in your application, you may pass a boolean value as the second argument to the `attempt` method, which will keep the user authenticated indefinitely, or until they manually logout. Your `users` table must include the string `remember_token` column, which will be used to store the "remember me" token.
+Many web applications provide a "remember me" checkbox on their login form. If you would like to provide "remember me" functionality in your application, you may pass a boolean value as the second argument to the `attempt` method.
- if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
- // The user is being remembered...
- }
+When this value is `true`, Laravel will keep the user authenticated indefinitely or until they manually logout. Your `users` table must include the string `remember_token` column, which will be used to store the "remember me" token. The `users` table migration included with new Laravel applications already includes this column:
-If you are "remembering" users, you may use the `viaRemember` method to determine if the user was authenticated using the "remember me" cookie:
+ use Illuminate\Support\Facades\Auth;
- if (Auth::viaRemember()) {
- //
+ if (Auth::attempt(['email' => $email, 'password' => $password], $remember)) {
+ // The user is being remembered...
}
@@ -331,31 +326,35 @@ If you are "remembering" users, you may use the `viaRemember` method to determin
#### Authenticate A User Instance
-If you need to log an existing user instance into your application, you may call the `login` method with the user instance. The given object must be an implementation of the `Illuminate\Contracts\Auth\Authenticatable` [contract](/docs/{{version}}/contracts). The `App\Models\User` model included with Laravel already implements this interface. This method of authentication is useful when you already have a valid user instance, such as directly after a user registers with your application:
+If you need to set an existing user instance as the currently authenticated user, you may pass the user instance to the `Auth` facade's `login` method. The given user instance must be an implementation of the `Illuminate\Contracts\Auth\Authenticatable` [contract](/docs/{{version}}/contracts). The `App\Models\User` model included with Laravel already implements this interface. This method of authentication is useful when you already have a valid user instance, such as directly after a user registers with your application:
+
+ use Illuminate\Support\Facades\Auth;
Auth::login($user);
- // Login and "remember" the given user...
- Auth::login($user, true);
+You may pass a boolean value as the second argument to the `login` method. This value indicates if "remember me" functionality is desired for the authenticated session. Remember, this means that the session will be authenticated indefinitely or until the user manually logs out of the application:
-You may specify the guard instance you would like to use:
+ Auth::login($user, $remember = true);
+
+If needed, you may specify an authentication guard before calling the `login` method:
Auth::guard('admin')->login($user);
#### Authenticate A User By ID
-To log a user into the application by their ID, you may use the `loginUsingId` method. This method accepts the primary key of the user you wish to authenticate:
+To authenticate a user using their database record's primary key, you may use the `loginUsingId` method. This method accepts the primary key of the user you wish to authenticate:
Auth::loginUsingId(1);
- // Login and "remember" the given user...
- Auth::loginUsingId(1, true);
+You may pass a boolean value as the second argument to the `loginUsingId` method. This value indicates if "remember me" functionality is desired for the authenticated session. Remember, this means that the session will be authenticated indefinitely or until the user manually logs out of the application:
+
+ Auth::loginUsingId(1, $remember = true);
#### Authenticate A User Once
-You may use the `once` method to log a user into the application for a single request. No sessions or cookies will be utilized, which means this method may be helpful when building a stateless API:
+You may use the `once` method to authenticate a user with the application for a single request. No sessions or cookies will be utilized when calling this method:
if (Auth::once($credentials)) {
//
@@ -364,18 +363,18 @@ You may use the `once` method to log a user into the application for a single re
## HTTP Basic Authentication
-[HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) provides a quick way to authenticate users of your application without setting up a dedicated "login" page. To get started, attach the `auth.basic` [middleware](/docs/{{version}}/middleware) to your route. The `auth.basic` middleware is included with the Laravel framework, so you do not need to define it:
+[HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) provides a quick way to authenticate users of your application without setting up a dedicated "login" page. To get started, attach the `auth.basic` [middleware](/docs/{{version}}/middleware) to a route. The `auth.basic` middleware is included with the Laravel framework, so you do not need to define it:
- Route::get('profile', function () {
- // Only authenticated users may enter...
+ Route::get('/profile', function () {
+ // Only authenticated users may access this route...
})->middleware('auth.basic');
-Once the middleware has been attached to the route, you will automatically be prompted for credentials when accessing the route in your browser. By default, the `auth.basic` middleware will use the `email` column on the user record as the "username".
+Once the middleware has been attached to the route, you will automatically be prompted for credentials when accessing the route in your browser. By default, the `auth.basic` middleware will assume the `email` column on your `users` database table is the user's "username".
#### A Note On FastCGI
-If you are using PHP FastCGI, HTTP Basic authentication may not work correctly out of the box. The following lines should be added to your `.htaccess` file:
+If you are using PHP FastCGI and Apache to serve your Laravel application, HTTP Basic authentication may not work correctly. To correct these problems, the following lines may be added to your application's `.htaccess` file:
RewriteCond %{HTTP:Authorization} ^(.+)$
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
@@ -383,7 +382,7 @@ If you are using PHP FastCGI, HTTP Basic authentication may not work correctly o
### Stateless HTTP Basic Authentication
-You may also use HTTP Basic Authentication without setting a user identifier cookie in the session, which is particularly useful for API authentication. To do so, [define a middleware](/docs/{{version}}/middleware) that calls the `onceBasic` method. If no response is returned by the `onceBasic` method, the request may be passed further into the application:
+You may also use HTTP Basic Authentication without setting a user identifier cookie in the session. This is primarily helpful if you choose to use HTTP Authentication to authenticate requests to your application's API. To accomplish this, [define a middleware](/docs/{{version}}/middleware) that calls the `onceBasic` method. If no response is returned by the `onceBasic` method, the request may be passed further into the application:
middleware('auth.basic.once');
## Logging Out
-To manually log users out of your application, you may use the `logout` method on the `Auth` facade. This will clear the authentication information in the user's session:
+To manually log users out of your application, you may use the `logout` method provided by the `Auth` facade. This will remove the authentication information from the user's session so that subsequent requests are not authenticated.
+In addition to calling the `logout` method, it is recommended that you invalidate the user's session and regenerate their [CSRF token](/docs/{{version}}/csrf). After logging the user out, you would typically redirect the user to the root of your application:
+
+ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
- Auth::logout();
+ /**
+ * Log the user out of the application.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\Response
+ */
+ public function logout(Request $request)
+ {
+ Auth::logout();
+
+ $request->session()->invalidate();
+
+ $request->session()->regenerateToken();
+
+ return redirect('/');
+ }
### Invalidating Sessions On Other Devices
Laravel also provides a mechanism for invalidating and "logging out" a user's sessions that are active on other devices without invalidating the session on their current device. This feature is typically utilized when a user is changing or updating their password and you would like to invalidate sessions on other devices while keeping the current device authenticated.
-Before getting started, you should make sure that the `Illuminate\Session\Middleware\AuthenticateSession` middleware is present and un-commented in your `app/Http/Kernel.php` class' `web` middleware group:
+Before getting started, you should make sure that the `Illuminate\Session\Middleware\AuthenticateSession` middleware is present and un-commented in your `App\Http\Kernel` class' `web` middleware group:
'web' => [
// ...
@@ -435,27 +452,25 @@ Before getting started, you should make sure that the `Illuminate\Session\Middle
// ...
],
-Then, you may use the `logoutOtherDevices` method on the `Auth` facade. This method requires the user to provide their current password, which your application should accept through an input form:
+Then, you may use the `logoutOtherDevices` method provided by the `Auth` facade. This method requires the user to confirm their current password, which your application should accept through an input form:
use Illuminate\Support\Facades\Auth;
- Auth::logoutOtherDevices($password);
+ Auth::logoutOtherDevices($currentPassword);
When the `logoutOtherDevices` method is invoked, the user's other sessions will be invalidated entirely, meaning they will be "logged out" of all guards they were previously authenticated by.
-> {note} When using the `AuthenticateSession` middleware in combination with a custom route name for the `login` route, you must override the `unauthenticated` method on your application's exception handler to properly redirect users to your login page.
-
## Password Confirmation
-While building your application, you may occasionally have actions that should require the user to confirm their password before the action is performed. Laravel includes built-in middleware to make this process a breeze. Implementing this feature will require you to define two routes: one route to display a view asking the user to confirm their password, and one route to confirm that the password is valid and redirect the user to their intended destination.
+While building your application, you may occasionally have actions that should require the user to confirm their password before the action is performed or before the user is redirected to a sensitive area of the application. Laravel includes built-in middleware to make this process a breeze. Implementing this feature will require you to define two routes: one route to display a view asking the user to confirm their password and another route to confirm that the password is valid and redirect the user to their intended destination.
-> {tip} The following documentation discusses how to integrate with Laravel's password confirmation features directly; however, if you would like to get started more quickly, the [Laravel Jetstream](https://jetstream.laravel.com) authentication scaffolding package includes support for this feature!
+> {tip} The following documentation discusses how to integrate with Laravel's password confirmation features directly; however, if you would like to get started more quickly, the [Laravel application starter kits](/docs/{{version}}/starter-kits) include support for this feature!
### Configuration
-After confirming their password, a user will not be asked to confirm their password again for three hours. However, you may configure the length of time before the user is re-prompted for their password by changing the value of the `password_timeout` configuration value within your `auth` configuration file.
+After confirming their password, a user will not be asked to confirm their password again for three hours. However, you may configure the length of time before the user is re-prompted for their password by changing the value of the `password_timeout` configuration value within your application's `config/auth.php` configuration file.
### Routing
@@ -463,11 +478,11 @@ After confirming their password, a user will not be asked to confirm their passw
#### The Password Confirmation Form
-First, we will define the route that is needed to display a view requesting that the user confirm their password:
+First, we will define a route to display a view that requests that the user confirm their password:
Route::get('/confirm-password', function () {
return view('auth.confirm-password');
- })->middleware(['auth'])->name('password.confirm');
+ })->middleware('auth')->name('password.confirm');
As you might expect, the view that is returned by this route should have a form containing a `password` field. In addition, feel free to include text within the view that explains that the user is entering a protected area of the application and must confirm their password.
@@ -478,6 +493,7 @@ Next, we will define a route that will handle the form request from the "confirm
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
+ use Illuminate\Support\Facades\Redirect;
Route::post('/confirm-password', function (Request $request) {
if (! Hash::check($request->password, $request->user()->password)) {
@@ -491,12 +507,12 @@ Next, we will define a route that will handle the form request from the "confirm
return redirect()->intended();
})->middleware(['auth', 'throttle:6,1'])->name('password.confirm');
-Before moving on, let's examine this route in more detail. First, the request's `password` attribute is determined to actually match the authenticated user's password. If the password is valid, we need to inform Laravel's session that the user has confirmed their password. The `passwordConfirmed` method will set a timestamp in the user's session that Laravel can use to determine when the user last confirmed their password. Finally, we can redirect the user to their intended destination.
+Before moving on, let's examine this route in more detail. First, the request's `password` field is determined to actually match the authenticated user's password. If the password is valid, we need to inform Laravel's session that the user has confirmed their password. The `passwordConfirmed` method will set a timestamp in the user's session that Laravel can use to determine when the user last confirmed their password. Finally, we can redirect the user to their intended destination.
### Protecting Routes
-You should ensure that any route that performs an action that should require recent password confirmation is assigned the `password.confirm` middleware. This middleware is included with the default installation of Laravel and will automatically store the user's intended destination in the session so that the user may be redirected to that location after confirming their password. After storing the user's intended destination in the session, the middleware will redirect the user to the `password.confirm` [named route](/docs/{{version}}/routing#named-routes):
+You should ensure that any route that performs an action which requires recent password confirmation is assigned the `password.confirm` middleware. This middleware is included with the default installation of Laravel and will automatically store the user's intended destination in the session so that the user may be redirected to that location after confirming their password. After storing the user's intended destination in the session, the middleware will redirect the user to the `password.confirm` [named route](/docs/{{version}}/routing#named-routes):
Route::get('/settings', function () {
// ...
@@ -509,7 +525,7 @@ You should ensure that any route that performs an action that should require rec
## Adding Custom Guards
-You may define your own authentication guards using the `extend` method on the `Auth` facade. You should place this call to `extend` within a [service provider](/docs/{{version}}/providers). Since Laravel already ships with an `AuthServiceProvider`, we can place the code in that provider:
+You may define your own authentication guards using the `extend` method on the `Auth` facade. You should place your call to the `extend` method within a [service provider](/docs/{{version}}/providers). Since Laravel already ships with an `AuthServiceProvider`, we can place the code in that provider:
[
'api' => [
@@ -550,9 +566,9 @@ As you can see in the example above, the callback passed to the `extend` method
### Closure Request Guards
-The simplest way to implement a custom, HTTP request based authentication system is by using the `Auth::viaRequest` method. This method allows you to quickly define your authentication process using a single Closure.
+The simplest way to implement a custom, HTTP request based authentication system is by using the `Auth::viaRequest` method. This method allows you to quickly define your authentication process using a single closure.
-To get started, call the `Auth::viaRequest` method within the `boot` method of your `AuthServiceProvider`. The `viaRequest` method accepts an authentication driver name as its first argument. This name can be any string that describes your custom guard. The second argument passed to the method should be a Closure that receives the incoming HTTP request and returns a user instance or, if authentication fails, `null`:
+To get started, call the `Auth::viaRequest` method within the `boot` method of your `AuthServiceProvider`. The `viaRequest` method accepts an authentication driver name as its first argument. This name can be any string that describes your custom guard. The second argument passed to the method should be a closure that receives the incoming HTTP request and returns a user instance or, if authentication fails, `null`:
use App\Models\User;
use Illuminate\Http\Request;
@@ -567,12 +583,12 @@ To get started, call the `Auth::viaRequest` method within the `boot` method of y
{
$this->registerPolicies();
- Auth::viaRequest('custom-token', function ($request) {
+ Auth::viaRequest('custom-token', function (Request $request) {
return User::where('token', $request->token)->first();
});
}
-Once your custom authentication driver has been defined, you use it as a driver within the `guards` configuration of your `auth.php` configuration file:
+Once your custom authentication driver has been defined, you may configure it as a driver within the `guards` configuration of your `auth.php` configuration file:
'guards' => [
'api' => [
@@ -583,13 +599,13 @@ Once your custom authentication driver has been defined, you use it as a driver
## Adding Custom User Providers
-If you are not using a traditional relational database to store your users, you will need to extend Laravel with your own authentication user provider. We will use the `provider` method on the `Auth` facade to define a custom user provider:
+If you are not using a traditional relational database to store your users, you will need to extend Laravel with your own authentication user provider. We will use the `provider` method on the `Auth` facade to define a custom user provider. The user provider resolver should return an implementation of `Illuminate\Contracts\Auth\UserProvider`:
registerPolicies();
- Auth::provider('riak', function ($app, array $config) {
+ Auth::provider('mongo', function ($app, array $config) {
// Return an instance of Illuminate\Contracts\Auth\UserProvider...
- return new RiakUserProvider($app->make('riak.connection'));
+ return new MongoUserProvider($app->make('mongo.connection'));
});
}
}
@@ -616,11 +632,11 @@ After you have registered the provider using the `provider` method, you may swit
'providers' => [
'users' => [
- 'driver' => 'riak',
+ 'driver' => 'mongo',
],
],
-Finally, you may use this provider in your `guards` configuration:
+Finally, you may reference this provider in your `guards` configuration:
'guards' => [
'web' => [
@@ -632,7 +648,7 @@ Finally, you may use this provider in your `guards` configuration:
### The User Provider Contract
-The `Illuminate\Contracts\Auth\UserProvider` implementations are only responsible for fetching an `Illuminate\Contracts\Auth\Authenticatable` implementation out of a persistent storage system, such as MySQL, Riak, etc. These two interfaces allow the Laravel authentication mechanisms to continue functioning regardless of how the user data is stored or what type of class is used to represent it.
+`Illuminate\Contracts\Auth\UserProvider` implementations are responsible for fetching an `Illuminate\Contracts\Auth\Authenticatable` implementation out of a persistent storage system, such as MySQL, MongoDB, etc. These two interfaces allow the Laravel authentication mechanisms to continue functioning regardless of how the user data is stored or what type of class is used to represent the authenticated user:
Let's take a look at the `Illuminate\Contracts\Auth\UserProvider` contract:
@@ -651,18 +667,18 @@ Let's take a look at the `Illuminate\Contracts\Auth\UserProvider` contract:
The `retrieveById` function typically receives a key representing the user, such as an auto-incrementing ID from a MySQL database. The `Authenticatable` implementation matching the ID should be retrieved and returned by the method.
-The `retrieveByToken` function retrieves a user by their unique `$identifier` and "remember me" `$token`, stored in a field `remember_token`. As with the previous method, the `Authenticatable` implementation should be returned.
+The `retrieveByToken` function retrieves a user by their unique `$identifier` and "remember me" `$token`, typically stored in a database column like `remember_token`. As with the previous method, the `Authenticatable` implementation with a matching token value should be returned by this method.
-The `updateRememberToken` method updates the `$user` field `remember_token` with the new `$token`. A fresh token is assigned on a successful "remember me" login attempt or when the user is logging out.
+The `updateRememberToken` method updates the `$user` instance's `remember_token` with the new `$token`. A fresh token is assigned to users on a successful "remember me" authentication attempt or when the user is logging out.
-The `retrieveByCredentials` method receives the array of credentials passed to the `Auth::attempt` method when attempting to sign into an application. The method should then "query" the underlying persistent storage for the user matching those credentials. Typically, this method will run a query with a "where" condition on `$credentials['username']`. The method should then return an implementation of `Authenticatable`. **This method should not attempt to do any password validation or authentication.**
+The `retrieveByCredentials` method receives the array of credentials passed to the `Auth::attempt` method when attempting to authenticate with an application. The method should then "query" the underlying persistent storage for the user matching those credentials. Typically, this method will run a query with a "where" condition that searches for a user record with a "username" matching the value of `$credentials['username']`. The method should return an implementation of `Authenticatable`. **This method should not attempt to do any password validation or authentication.**
-The `validateCredentials` method should compare the given `$user` with the `$credentials` to authenticate the user. For example, this method should probably use `Hash::check` to compare the value of `$user->getAuthPassword()` to the value of `$credentials['password']`. This method should return `true` or `false` indicating on whether the password is valid.
+The `validateCredentials` method should compare the given `$user` with the `$credentials` to authenticate the user. For example, this method will typically use the `Hash::check` method to compare the value of `$user->getAuthPassword()` to the value of `$credentials['password']`. This method should return `true` or `false` indicating whether the password is valid.
### The Authenticatable Contract
-Now that we have explored each of the methods on the `UserProvider`, let's take a look at the `Authenticatable` contract. Remember, the provider should return implementations of this interface from the `retrieveById`, `retrieveByToken`, and `retrieveByCredentials` methods:
+Now that we have explored each of the methods on the `UserProvider`, let's take a look at the `Authenticatable` contract. Remember, user providers should return implementations of this interface from the `retrieveById`, `retrieveByToken`, and `retrieveByCredentials` methods:
## Events
-Laravel raises a variety of [events](/docs/{{version}}/events) during the authentication process. You may attach listeners to these events in your `EventServiceProvider`:
+Laravel dispatches a variety of [events](/docs/{{version}}/events) during the authentication process. You may attach listeners to these events in your `EventServiceProvider`:
/**
* The event listener mappings for the application.
diff --git a/authorization.md b/authorization.md
index c326a7cc84e..0da25bb2563 100644
--- a/authorization.md
+++ b/authorization.md
@@ -17,19 +17,19 @@
- [Policy Filters](#policy-filters)
- [Authorizing Actions Using Policies](#authorizing-actions-using-policies)
- [Via The User Model](#via-the-user-model)
- - [Via Middleware](#via-middleware)
- [Via Controller Helpers](#via-controller-helpers)
+ - [Via Middleware](#via-middleware)
- [Via Blade Templates](#via-blade-templates)
- [Supplying Additional Context](#supplying-additional-context)
## Introduction
-In addition to providing [authentication](/docs/{{version}}/authentication) services out of the box, Laravel also provides a simple way to authorize user actions against a given resource. Like authentication, Laravel's approach to authorization is simple, and there are two primary ways of authorizing actions: gates and policies.
+In addition to providing built-in [authentication](/docs/{{version}}/authentication) services, Laravel also provides a simple way to authorize user actions against a given resource. For example, even though a user is authenticated, they may not be authorized to update or delete certain Eloquent models or database records managed by your application. Laravel's authorization features provide an easy, organized way of managing these types of authorization checks.
-Think of gates and policies like routes and controllers. Gates provide a simple, Closure based approach to authorization while policies, like controllers, group their logic around a particular model or resource. We'll explore gates first and then examine policies.
+Laravel provides two primary ways of authorizing actions: [gates](#gates) and [policies](#creating-policies). Think of gates and policies like routes and controllers. Gates provide a simple, closure based approach to authorization while policies, like controllers, group logic around a particular model or resource. In this documentation, we'll explore gates first and then examine policies.
-You do not need to choose between exclusively using gates or exclusively using policies when building an application. Most applications will most likely contain a mixture of gates and policies, and that is perfectly fine! Gates are most applicable to actions which are not related to any model or resource, such as viewing an administrator dashboard. In contrast, policies should be used when you wish to authorize an action for a particular model or resource.
+You do not need to choose between exclusively using gates or exclusively using policies when building an application. Most applications will most likely contain some mixture of gates and policies, and that is perfectly fine! Gates are most applicable to actions which are not related to any model or resource, such as viewing an administrator dashboard. In contrast, policies should be used when you wish to authorize an action for a particular model or resource.
## Gates
@@ -37,7 +37,15 @@ You do not need to choose between exclusively using gates or exclusively using p
### Writing Gates
-Gates are Closures that determine if a user is authorized to perform a given action and are typically defined in the `App\Providers\AuthServiceProvider` class using the `Gate` facade. Gates always receive a user instance as their first argument, and may optionally receive additional arguments such as a relevant Eloquent model:
+> {note} Gates are a great way to learn the basics of Laravel's authorization features; however, when building robust Laravel applications you should consider using [policies](#creating-policies) to organize your authorization rules.
+
+Gates are simply closures that determine if a user is authorized to perform a given action. Typically, gates are defined within the `boot` method of the `App\Providers\AuthServiceProvider` class using the `Gate` facade. Gates always receive a user instance as their first argument and may optionally receive additional arguments such as a relevant Eloquent model.
+
+In this example, we'll define a gate to determine if a user can update a given `App\Models\Post` model. The gate will accomplish this may comparing the user's `id` against the `user_id` of the user that created the post:
+
+ use App\Models\Post;
+ use App\Models\User;
+ use Illuminate\Support\Facades\Gate;
/**
* Register any authentication / authorization services.
@@ -48,18 +56,15 @@ Gates are Closures that determine if a user is authorized to perform a given act
{
$this->registerPolicies();
- Gate::define('edit-settings', function ($user) {
- return $user->isAdmin;
- });
-
- Gate::define('update-post', function ($user, $post) {
+ Gate::define('update-post', function (User $user, Post $post) {
return $user->id === $post->user_id;
});
}
-Gates may also be defined using a class callback array, like controllers:
+Like controllers, gates may also be defined using a class callback array:
use App\Policies\PostPolicy;
+ use Illuminate\Support\Facades\Gate;
/**
* Register any authentication / authorization services.
@@ -76,21 +81,37 @@ Gates may also be defined using a class callback array, like controllers:
### Authorizing Actions
-To authorize an action using gates, you should use the `allows` or `denies` methods. Note that you are not required to pass the currently authenticated user to these methods. Laravel will automatically take care of passing the user into the gate Closure:
+To authorize an action using gates, you should use the `allows` or `denies` methods provided by the `Gate` facade. Note that you are not required to pass the currently authenticated user to these methods. Laravel will automatically take care of passing the user into the gate closure. It is typical to call the gate authorization methods within your application's controllers before performing an action that requires authorization:
- if (Gate::allows('edit-settings')) {
- // The current user can edit settings
- }
+ allows('update-post', $post)) {
// The user can update the post...
@@ -100,20 +121,20 @@ If you would like to determine if a particular user is authorized to perform an
// The user can't update the post...
}
-You may authorize multiple actions at a time with the `any` or `none` methods:
+You may authorize multiple actions at a time using the `any` or `none` methods:
if (Gate::any(['update-post', 'delete-post'], $post)) {
- // The user can update or delete the post
+ // The user can update or delete the post...
}
if (Gate::none(['update-post', 'delete-post'], $post)) {
- // The user cannot update or delete the post
+ // The user can't update or delete the post...
}
#### Authorizing Or Throwing Exceptions
-If you would like to attempt to authorize an action and automatically throw an `Illuminate\Auth\Access\AuthorizationException` if the user is not allowed to perform the given action, you may use the `Gate::authorize` method. Instances of `AuthorizationException` are automatically converted to `403` HTTP response:
+If you would like to attempt to authorize an action and automatically throw an `Illuminate\Auth\Access\AuthorizationException` if the user is not allowed to perform the given action, you may use the `Gate` facade's `authorize` method. Instances of `AuthorizationException` are automatically converted to a 403 HTTP response by Laravel's exception handler:
Gate::authorize('update-post', $post);
@@ -122,13 +143,23 @@ If you would like to attempt to authorize an action and automatically throw an `
#### Supplying Additional Context
-The gate methods for authorizing abilities (`allows`, `denies`, `check`, `any`, `none`, `authorize`, `can`, `cannot`) and the authorization [Blade directives](#via-blade-templates) (`@can`, `@cannot`, `@canany`) can receive an array as the second argument. These array elements are passed as parameters to gate, and can be used for additional context when making authorization decisions:
+The gate methods for authorizing abilities (`allows`, `denies`, `check`, `any`, `none`, `authorize`, `can`, `cannot`) and the authorization [Blade directives](#via-blade-templates) (`@can`, `@cannot`, `@canany`) can receive an array as their second argument. These array elements are passed as parameters to the gate closure, and can be used for additional context when making authorization decisions:
+
+ use App\Models\Category;
+ use App\Models\User;
+ use Illuminate\Support\Facades\Gate;
+
+ Gate::define('create-post', function (User $user, Category $category, $pinned) {
+ if (! $user->canPublishToGroup($category->group)) {
+ return false;
+ } elseif ($pinned && ! $user->canPinPosts()) {
+ return false;
+ }
- Gate::define('create-post', function ($user, $category, $extraFlag) {
- return $category->group > 3 && $extraFlag === true;
+ return true;
});
- if (Gate::check('create-post', [$category, $extraFlag])) {
+ if (Gate::check('create-post', [$category, $pinned])) {
// The user can create the post...
}
@@ -137,18 +168,19 @@ The gate methods for authorizing abilities (`allows`, `denies`, `check`, `any`,
So far, we have only examined gates that return simple boolean values. However, sometimes you may wish to return a more detailed response, including an error message. To do so, you may return an `Illuminate\Auth\Access\Response` from your gate:
+ use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;
- Gate::define('edit-settings', function ($user) {
+ Gate::define('edit-settings', function (User $user) {
return $user->isAdmin
? Response::allow()
- : Response::deny('You must be a super administrator.');
+ : Response::deny('You must be an administrator.');
});
-When returning an authorization response from your gate, the `Gate::allows` method will still return a simple boolean value; however, you may use the `Gate::inspect` method to get the full authorization response returned by the gate:
+Even when you return an authorization response from your gate, the `Gate::allows` method will still return a simple boolean value; however, you may use the `Gate::inspect` method to get the full authorization response returned by the gate:
- $response = Gate::inspect('edit-settings', $post);
+ $response = Gate::inspect('edit-settings');
if ($response->allowed()) {
// The action is authorized...
@@ -156,34 +188,36 @@ When returning an authorization response from your gate, the `Gate::allows` meth
echo $response->message();
}
-Of course, when using the `Gate::authorize` method to throw an `AuthorizationException` if the action is not authorized, the error message provided by the authorization response will be propagated to the HTTP response:
+When using the `Gate::authorize` method, which throws an `AuthorizationException` if the action is not authorized, the error message provided by the authorization response will be propagated to the HTTP response:
- Gate::authorize('edit-settings', $post);
+ Gate::authorize('edit-settings');
// The action is authorized...
### Intercepting Gate Checks
-Sometimes, you may wish to grant all abilities to a specific user. You may use the `before` method to define a callback that is run before all other authorization checks:
+Sometimes, you may wish to grant all abilities to a specific user. You may use the `before` method to define a closure that is run before all other authorization checks:
+
+ use Illuminate\Support\Facades\Gate;
Gate::before(function ($user, $ability) {
- if ($user->isSuperAdmin()) {
+ if ($user->isAdministrator()) {
return true;
}
});
-If the `before` callback returns a non-null result that result will be considered the result of the check.
+If the `before` closure returns a non-null result that result will be considered the result of the authorization check.
-You may use the `after` method to define a callback to be executed after all other authorization checks:
+You may use the `after` method to define a closure to be executed after all other authorization checks:
Gate::after(function ($user, $ability, $result, $arguments) {
- if ($user->isSuperAdmin()) {
+ if ($user->isAdministrator()) {
return true;
}
});
-Similar to the `before` check, if the `after` callback returns a non-null result that result will be considered the result of the check.
+Similar to the `before` method, if the `after` closure returns a non-null result that result will be considered the result of the authorization check.
## Creating Policies
@@ -191,22 +225,22 @@ Similar to the `before` check, if the `after` callback returns a non-null result
### Generating Policies
-Policies are classes that organize authorization logic around a particular model or resource. For example, if your application is a blog, you may have a `Post` model and a corresponding `PostPolicy` to authorize user actions such as creating or updating posts.
+Policies are classes that organize authorization logic around a particular model or resource. For example, if your application is a blog, you may have a `App\Models\Post` model and a corresponding `App\Policies\PostPolicy` to authorize user actions such as creating or updating posts.
-You may generate a policy using the `make:policy` [artisan command](/docs/{{version}}/artisan). The generated policy will be placed in the `app/Policies` directory. If this directory does not exist in your application, Laravel will create it for you:
+You may generate a policy using the `make:policy` Artisan command. The generated policy will be placed in the `app/Policies` directory. If this directory does not exist in your application, Laravel will create it for you:
php artisan make:policy PostPolicy
-The `make:policy` command will generate an empty policy class. If you would like to generate a class with the basic "CRUD" policy methods already included in the class, you may specify a `--model` when executing the command:
+The `make:policy` command will generate an empty policy class. If you would like to generate a class with example policy methods related to viewing, creating, updating, and deleting the resource, you may provide a `--model` option when executing the command:
php artisan make:policy PostPolicy --model=Post
-> {tip} All policies are resolved via the Laravel [service container](/docs/{{version}}/container), allowing you to type-hint any needed dependencies in the policy's constructor to have them automatically injected.
-
### Registering Policies
-Once the policy exists, it needs to be registered. The `AuthServiceProvider` included with fresh Laravel applications contains a `policies` property which maps your Eloquent models to their corresponding policies. Registering a policy will instruct Laravel which policy to utilize when authorizing actions against a given model:
+Once the policy class has been created, it needs to be registered. Registering policies is how we can inform Laravel which policy to use when authorizing actions against a given model type.
+
+The `App\Providers\AuthServiceProvider` included with fresh Laravel applications contains a `policies` property which maps your Eloquent models to their corresponding policies. Registering a policy will instruct Laravel which policy to utilize when authorizing actions against a given Eloquent model:
#### Policy Auto-Discovery
-Instead of manually registering model policies, Laravel can auto-discover policies as long as the model and policy follow standard Laravel naming conventions. Specifically, the policies must be in a `Policies` directory at or above the directory that contains your models. So, for example, the models may be placed in the `app/Models` directory while the policies may be placed in the `app/Policies` directory. In this situation, Laravel will check for policies in `app/Models/Policies` then `app/Policies`. In addition, the policy name must match the model name and have a `Policy` suffix. So, a `User` model would correspond to a `UserPolicy` class.
+Instead of manually registering model policies, Laravel can automatically discover policies as long as the model and policy follow standard Laravel naming conventions. Specifically, the policies must be in a `Policies` directory at or above the directory that contains your models. So, for example, the models may be placed in the `app/Models` directory while the policies may be placed in the `app/Policies` directory. In this situation, Laravel will check for policies in `app/Models/Policies` then `app/Policies`. In addition, the policy name must match the model name and have a `Policy` suffix. So, a `User` model would correspond to a `UserPolicy` policy class.
-If you would like to provide your own policy discovery logic, you may register a custom callback using the `Gate::guessPolicyNamesUsing` method. Typically, this method should be called from the `boot` method of your application's `AuthServiceProvider`:
+If you would like to define your own policy discovery logic, you may register a custom policy discovery callback using the `Gate::guessPolicyNamesUsing` method. Typically, this method should be called from the `boot` method of your application's `AuthServiceProvider`:
use Illuminate\Support\Facades\Gate;
Gate::guessPolicyNamesUsing(function ($modelClass) {
- // return policy class name...
+ // Return the name of the policy class for the given model...
});
> {note} Any policies that are explicitly mapped in your `AuthServiceProvider` will take precedence over any potentially auto-discovered policies.
@@ -262,9 +296,9 @@ If you would like to provide your own policy discovery logic, you may register a
### Policy Methods
-Once the policy has been registered, you may add methods for each action it authorizes. For example, let's define an `update` method on our `PostPolicy` which determines if a given `User` can update a given `Post` instance.
+Once the policy class has been registered, you may add methods for each action it authorizes. For example, let's define an `update` method on our `PostPolicy` which determines if a given `App\Models\User` can update a given `App\Models\Post` instance.
-The `update` method will receive a `User` and a `Post` instance as its arguments, and should return `true` or `false` indicating whether the user is authorized to update the given `Post`. So, for this example, let's verify that the user's `id` matches the `user_id` on the post:
+The `update` method will receive a `User` and a `Post` instance as its arguments, and should return `true` or `false` indicating whether the user is authorized to update the given `Post`. So, in this example, we will verify that the user's `id` matches the `user_id` on the post:
{tip} If you used the `--model` option when generating your policy via the Artisan console, it will already contain methods for the `viewAny`, `view`, `create`, `update`, `delete`, `restore`, and `forceDelete` actions.
+If you used the `--model` option when generating your policy via the Artisan console, it will already contain methods for the `viewAny`, `view`, `create`, `update`, `delete`, `restore`, and `forceDelete` actions.
+
+> {tip} All policies are resolved via the Laravel [service container](/docs/{{version}}/container), allowing you to type-hint any needed dependencies in the policy's constructor to have them automatically injected.
### Policy Responses
-So far, we have only examined policy methods that return simple boolean values. However, sometimes you may wish to return a more detailed response, including an error message. To do so, you may return an `Illuminate\Auth\Access\Response` from your policy method:
+So far, we have only examined policy methods that return simple boolean values. However, sometimes you may wish to return a more detailed response, including an error message. To do so, you may return an `Illuminate\Auth\Access\Response` instance from your policy method:
+ use App\Models\Post;
+ use App\Models\User;
use Illuminate\Auth\Access\Response;
/**
@@ -315,6 +353,8 @@ So far, we have only examined policy methods that return simple boolean values.
When returning an authorization response from your policy, the `Gate::allows` method will still return a simple boolean value; however, you may use the `Gate::inspect` method to get the full authorization response returned by the gate:
+ use Illuminate\Support\Facades\Gate;
+
$response = Gate::inspect('update', $post);
if ($response->allowed()) {
@@ -323,7 +363,7 @@ When returning an authorization response from your policy, the `Gate::allows` me
echo $response->message();
}
-Of course, when using the `Gate::authorize` method to throw an `AuthorizationException` if the action is not authorized, the error message provided by the authorization response will be propagated to the HTTP response:
+When using the `Gate::authorize` method, which throws an `AuthorizationException` if the action is not authorized, the error message provided by the authorization response will be propagated to the HTTP response:
Gate::authorize('update', $post);
@@ -332,9 +372,7 @@ Of course, when using the `Gate::authorize` method to throw an `AuthorizationExc
### Methods Without Models
-Some policy methods only receive the currently authenticated user and not an instance of the model they authorize. This situation is most common when authorizing `create` actions. For example, if you are creating a blog, you may wish to check if a user is authorized to create any posts at all.
-
-When defining policy methods that will not receive a model instance, such as a `create` method, it will not receive a model instance. Instead, you should define the method as only expecting the authenticated user:
+Some policy methods only receive an instance of the currently authenticated user. This situation is most common when authorizing `create` actions. For example, if you are creating a blog, you may wish to determine if a user is authorized to create any posts at all. In these situations, your policy method should only expect to receive a user instance:
/**
* Determine if the given user can create posts.
@@ -344,7 +382,7 @@ When defining policy methods that will not receive a model instance, such as a `
*/
public function create(User $user)
{
- //
+ return $user->role == 'writer';
}
@@ -379,14 +417,23 @@ By default, all gates and policies automatically return `false` if the incoming
For certain users, you may wish to authorize all actions within a given policy. To accomplish this, define a `before` method on the policy. The `before` method will be executed before any other methods on the policy, giving you an opportunity to authorize the action before the intended policy method is actually called. This feature is most commonly used for authorizing application administrators to perform any action:
- public function before($user, $ability)
+ use App\Models\User;
+
+ /**
+ * Perform pre-authorization checks.
+ *
+ * @param \App\Models\User $user
+ * @param string $ability
+ * @return void|bool
+ */
+ public function before(User $user, $ability)
{
- if ($user->isSuperAdmin()) {
+ if ($user->isAdministrator()) {
return true;
}
}
-If you would like to deny all authorizations for a user you should return `false` from the `before` method. If `null` is returned, the authorization will fall through to the policy method.
+If you would like to deny all authorization checks for a particular type of user then you may return `false` from the `before` method. If `null` is returned, the authorization check will fall through to the policy method.
> {note} The `before` method of a policy class will not be called if the class doesn't contain a method with a name matching the name of the ability being checked.
@@ -396,51 +443,74 @@ If you would like to deny all authorizations for a user you should return `false
### Via The User Model
-The `User` model that is included with your Laravel application includes two helpful methods for authorizing actions: `can` and `cant`. The `can` method receives the action you wish to authorize and the relevant model. For example, let's determine if a user is authorized to update a given `Post` model:
+The `App\Models\User` model that is included with your Laravel application includes two helpful methods for authorizing actions: `can` and `cant`. The `can` method receives the name of the action you wish to authorize and the relevant model. For example, let's determine if a user is authorized to update a given `App\Models\Post` model. Typically, this will be done within a controller method:
- if ($user->can('update', $post)) {
- //
- }
-
-If a [policy is registered](#registering-policies) for the given model, the `can` method will automatically call the appropriate policy and return the boolean result. If no policy is registered for the model, the `can` method will attempt to call the Closure based Gate matching the given action name.
-
-
-#### Actions That Don't Require Models
+ can('create', Post::class)) {
- // Executes the "create" method on the relevant policy...
+ class PostController extends Controller
+ {
+ /**
+ * Update the given post.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \App\Models\Post $post
+ * @return \Illuminate\Http\Response
+ */
+ public function update(Request $request, Post $post)
+ {
+ if (! $request->user()->can('update', $post)) {
+ abort(403);
+ }
+
+ // Update the post...
+ }
}
-
-### Via Middleware
+If a [policy is registered](#registering-policies) for the given model, the `can` method will automatically call the appropriate policy and return the boolean result. If no policy is registered for the model, the `can` method will attempt to call the closure based Gate matching the given action name.
-Laravel includes a middleware that can authorize actions before the incoming request even reaches your routes or controllers. By default, the `Illuminate\Auth\Middleware\Authorize` middleware is assigned the `can` key in your `App\Http\Kernel` class. Let's explore an example of using the `can` middleware to authorize that a user can update a blog post:
+
+#### Actions That Don't Require Models
- use App\Models\Post;
+Remember, some actions may correspond to policy methods like `create` that do not require a model instance. In these situations, you may pass a class name to the `can` method. The class name will be used to determine which policy to use when authorizing the action:
- Route::put('/post/{post}', function (Post $post) {
- // The current user may update the post...
- })->middleware('can:update,post');
+
-#### Actions That Don't Require Models
+ use App\Http\Controllers\Controller;
+ use App\Models\Post;
+ use Illuminate\Http\Request;
-Again, some actions like `create` may not require a model instance. In these situations, you may pass a class name to the middleware. The class name will be used to determine which policy to use when authorizing the action:
+ class PostController extends Controller
+ {
+ /**
+ * Create a post.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\Response
+ */
+ public function store(Request $request)
+ {
+ if (! $request->user()->can('create', Post::class)) {
+ abort(403);
+ }
- Route::post('/post', function () {
- // The current user may create posts...
- })->middleware('can:create,App\Models\Post');
+ // Create the post...
+ }
+ }
### Via Controller Helpers
-In addition to helpful methods provided to the `User` model, Laravel provides a helpful `authorize` method to any of your controllers which extend the `App\Http\Controllers\Controller` base class. Like the `can` method, this method accepts the name of the action you wish to authorize and the relevant model. If the action is not authorized, the `authorize` method will throw an `Illuminate\Auth\Access\AuthorizationException`, which the default Laravel exception handler will convert to an HTTP response with a `403` status code:
+In addition to helpful methods provided to the `App\Models\User` model, Laravel provides a helpful `authorize` method to any of your controllers which extend the `App\Http\Controllers\Controller` base class.
+
+Like the `can` method, this method accepts the name of the action you wish to authorize and the relevant model. If the action is not authorized, the `authorize` method will throw an `Illuminate\Auth\Access\AuthorizationException` exception which the Laravel exception handler will automatically convert to an HTTP response with a 403 status code:
#### Actions That Don't Require Models
-As previously discussed, some actions like `create` may not require a model instance. In these situations, you should pass a class name to the `authorize` method. The class name will be used to determine which policy to use when authorizing the action:
+As previously discussed, some policy methods like `create` do not require a model instance. In these situations, you should pass a class name to the `authorize` method. The class name will be used to determine which policy to use when authorizing the action:
+
+ use App\Models\Post;
+ use Illuminate\Http\Request;
/**
* Create a new blog post.
*
- * @param Request $request
- * @return Response
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\Response
+ *
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function create(Request $request)
@@ -490,9 +565,9 @@ As previously discussed, some actions like `create` may not require a model inst
#### Authorizing Resource Controllers
-If you are utilizing [resource controllers](/docs/{{version}}/controllers#resource-controllers), you may make use of the `authorizeResource` method in the controller's constructor. This method will attach the appropriate `can` middleware definitions to the resource controller's methods.
+If you are utilizing [resource controllers](/docs/{{version}}/controllers#resource-controllers), you may make use of the `authorizeResource` method in your controller's constructor. This method will attach the appropriate `can` middleware definitions to the resource controller's methods.
-The `authorizeResource` method accepts the model's class name as its first argument, and the name of the route / request parameter that will contain the model's ID as its second argument. You should ensure your [resource controller](/docs/{{version}}/controllers#resource-controllers) is created with the `--model` flag to have the required method signatures and type hints:
+The `authorizeResource` method accepts the model's class name as its first argument, and the name of the route / request parameter that will contain the model's ID as its second argument. You should ensure your [resource controller](/docs/{{version}}/controllers#resource-controllers) is created using the `--model` flag so that it has the required method signatures and type hints:
authorizeResource(Post::class, 'post');
}
}
-The following controller methods will be mapped to their corresponding policy method:
+The following controller methods will be mapped to their corresponding policy method. When requests are routed to the given controller method, the corresponding policy method will automatically be invoked before the controller method is executed:
| Controller Method | Policy Method |
| --- | --- |
@@ -524,53 +604,83 @@ The following controller methods will be mapped to their corresponding policy me
> {tip} You may use the `make:policy` command with the `--model` option to quickly generate a policy class for a given model: `php artisan make:policy PostPolicy --model=Post`.
+
+### Via Middleware
+
+Laravel includes a middleware that can authorize actions before the incoming request even reaches your routes or controllers. By default, the `Illuminate\Auth\Middleware\Authorize` middleware is assigned the `can` key in your `App\Http\Kernel` class. Let's explore an example of using the `can` middleware to authorize that a user can update a post:
+
+ use App\Models\Post;
+
+ Route::put('/post/{post}', function (Post $post) {
+ // The current user may update the post...
+ })->middleware('can:update,post');
+
+In this example, we're passing the `can` middleware two arguments. The first is the name of the action we wish to authorize and the second is the route parameter we wish to pass to the policy method. In this case, since we are using [implicit model binding](/docs/{{version}}/routing#implicit-binding), a `App\Models\Post` model will be passed to the policy method. If the user is not authorized to perform the given action, an HTTP response with a 403 status code will be returned by the middleware.
+
+
+#### Actions That Don't Require Models
+
+Again, some policy methods like `create` do not require a model instance. In these situations, you may pass a class name to the middleware. The class name will be used to determine which policy to use when authorizing the action:
+
+ Route::post('/post', function () {
+ // The current user may create posts...
+ })->middleware('can:create,App\Models\Post');
+
### Via Blade Templates
-When writing Blade templates, you may wish to display a portion of the page only if the user is authorized to perform a given action. For example, you may wish to show an update form for a blog post only if the user can actually update the post. In this situation, you may use the `@can` and `@cannot` family of directives:
+When writing Blade templates, you may wish to display a portion of the page only if the user is authorized to perform a given action. For example, you may wish to show an update form for a blog post only if the user can actually update the post. In this situation, you may use the `@can` and `@cannot` directives:
- @can('update', $post)
-
- @elsecan('create', App\Models\Post::class)
-
- @endcan
+```html
+@can('update', $post)
+
+@elsecan('create', App\Models\Post::class)
+
+@endcan
- @cannot('update', $post)
-
- @elsecannot('create', App\Models\Post::class)
-
- @endcannot
+@cannot('update', $post)
+
+@elsecannot('create', App\Models\Post::class)
+
+@endcannot
+```
-These directives are convenient shortcuts for writing `@if` and `@unless` statements. The `@can` and `@cannot` statements above respectively translate to the following statements:
+These directives are convenient shortcuts for writing `@if` and `@unless` statements. The `@can` and `@cannot` statements above are equivalent to the following statements:
- @if (Auth::user()->can('update', $post))
-
- @endif
+```html
+@if (Auth::user()->can('update', $post))
+
+@endif
- @unless (Auth::user()->can('update', $post))
-
- @endunless
+@unless (Auth::user()->can('update', $post))
+
+@endunless
+```
-You may also determine if a user has any authorization ability from a given list of abilities. To accomplish this, use the `@canany` directive:
+You may also determine if a user is authorized to perform any action from a given array of actions. To accomplish this, use the `@canany` directive:
- @canany(['update', 'view', 'delete'], $post)
- // The current user can update, view, or delete the post
- @elsecanany(['create'], \App\Models\Post::class)
- // The current user can create a post
- @endcanany
+```html
+@canany(['update', 'view', 'delete'], $post)
+
+@elsecanany(['create'], \App\Models\Post::class)
+
+@endcanany
+```
#### Actions That Don't Require Models
Like most of the other authorization methods, you may pass a class name to the `@can` and `@cannot` directives if the action does not require a model instance:
- @can('create', App\Models\Post::class)
-
- @endcan
+```html
+@can('create', App\Models\Post::class)
+
+@endcan
- @cannot('create', App\Models\Post::class)
-
- @endcannot
+@cannot('create', App\Models\Post::class)
+
+@endcannot
+```
### Supplying Additional Context
@@ -588,7 +698,7 @@ When authorizing actions using policies, you may pass an array as the second arg
public function update(User $user, Post $post, int $category)
{
return $user->id === $post->user_id &&
- $category > 3;
+ $user->canUpdateCategory($category);
}
When attempting to determine if the authenticated user can update a given post, we can invoke this policy method like so:
@@ -596,14 +706,15 @@ When attempting to determine if the authenticated user can update a given post,
/**
* Update the given blog post.
*
- * @param Request $request
- * @param Post $post
- * @return Response
+ * @param \Illuminate\Http\Request $request
+ * @param \App\Models\Post $post
+ * @return \Illuminate\Http\Response
+ *
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function update(Request $request, Post $post)
{
- $this->authorize('update', [$post, $request->input('category')]);
+ $this->authorize('update', [$post, $request->category]);
// The current user can update the blog post...
}
diff --git a/billing.md b/billing.md
index d01fe87ec8a..38309f1ab64 100644
--- a/billing.md
+++ b/billing.md
@@ -1,8 +1,9 @@
-# Laravel Cashier
+# Laravel Cashier (Stripe)
- [Introduction](#introduction)
- [Upgrading Cashier](#upgrading-cashier)
- [Installation](#installation)
+ - [Database Migrations](#database-migrations)
- [Configuration](#configuration)
- [Billable Model](#billable-model)
- [API Keys](#api-keys)
@@ -36,7 +37,6 @@
- [Extending Trials](#extending-trials)
- [Handling Stripe Webhooks](#handling-stripe-webhooks)
- [Defining Webhook Event Handlers](#defining-webhook-event-handlers)
- - [Failed Subscriptions](#handling-failed-subscriptions)
- [Verifying Webhook Signatures](#verifying-webhook-signatures)
- [Single Charges](#single-charges)
- [Simple Charge](#simple-charge)
@@ -67,30 +67,38 @@ When upgrading to a new version of Cashier, it's important that you carefully re
## Installation
-First, require the Cashier package for Stripe with Composer:
+First, install the Cashier package for Stripe using the Composer package manager:
composer require laravel/cashier
> {note} To ensure Cashier properly handles all Stripe events, remember to [set up Cashier's webhook handling](#handling-stripe-webhooks).
-#### Database Migrations
+### Database Migrations
-The Cashier service provider registers its own database migration directory, so remember to migrate your database after installing the package. The Cashier migrations will add several columns to your `users` table as well as create a new `subscriptions` table to hold all of your customer's subscriptions:
+Cashier's service provider registers its own database migration directory, so remember to migrate your database after installing the package. The Cashier migrations will add several columns to your `users` table as well as create a new `subscriptions` table to hold all of your customer's subscriptions:
php artisan migrate
-If you need to overwrite the migrations that ship with the Cashier package, you can publish them using the `vendor:publish` Artisan command:
+If you need to overwrite the migrations that ship with Cashier, you can publish them using the `vendor:publish` Artisan command:
php artisan vendor:publish --tag="cashier-migrations"
-If you would like to prevent Cashier's migrations from running entirely, you may use the `ignoreMigrations` provided by Cashier. Typically, this method should be called in the `register` method of your `AppServiceProvider`:
+If you would like to prevent Cashier's migrations from running entirely, you may use the `ignoreMigrations` method provided by Cashier. Typically, this method should be called in the `register` method of your `AppServiceProvider`:
use Laravel\Cashier\Cashier;
- Cashier::ignoreMigrations();
+ /**
+ * Register any application services.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ Cashier::ignoreMigrations();
+ }
-> {note} Stripe recommends that any column used for storing Stripe identifiers should be case-sensitive. Therefore, you should ensure the column collation for the `stripe_id` column is set to, for example, `utf8_bin` in MySQL. More info can be found [in the Stripe documentation](https://stripe.com/docs/upgrades#what-changes-does-stripe-consider-to-be-backwards-compatible).
+> {note} Stripe recommends that any column used for storing Stripe identifiers should be case-sensitive. Therefore, you should ensure the column collation for the `stripe_id` column is set to `utf8_bin` when using MySQL. More information regarding this can be found in the [Stripe documentation](https://stripe.com/docs/upgrades#what-changes-does-stripe-consider-to-be-backwards-compatible).
## Configuration
@@ -98,7 +106,7 @@ If you would like to prevent Cashier's migrations from running entirely, you may
### Billable Model
-Before using Cashier, add the `Billable` trait to your model definition. This trait provides various methods to allow you to perform common billing tasks, such as creating subscriptions, applying coupons, and updating payment method information:
+Before using Cashier, add the `Billable` trait to your billable model definition. Typically, this will be the `App\Models\User` model. This trait provides various methods to allow you to perform common billing tasks, such as creating subscriptions, applying coupons, and updating payment method information:
use Laravel\Cashier\Billable;
@@ -107,16 +115,16 @@ Before using Cashier, add the `Billable` trait to your model definition. This tr
use Billable;
}
-Cashier assumes your Billable model will be the `App\Models\User` class that ships with Laravel. If you wish to change this you can specify a different model in your `.env` file:
+Cashier assumes your billable model will be the `App\Models\User` class that ships with Laravel. If you wish to change this you can specify a different model in your `.env` file:
CASHIER_MODEL=App\Models\User
-> {note} If you're using a model other than Laravel's supplied `App\Models\User` model, you'll need to publish and alter the [migrations](#installation) provided to match your alternative model's table name.
+> {note} If you're using a model other than Laravel's supplied `App\Models\User` model, you'll need to publish and alter the [Cashier migrations](#installation) provided to match your alternative model's table name.
### API Keys
-Next, you should configure your Stripe keys in your `.env` file. You can retrieve your Stripe API keys from the Stripe control panel.
+Next, you should configure your Stripe API keys in your application's `.env` file. You can retrieve your Stripe API keys from the Stripe control panel:
STRIPE_KEY=your-stripe-key
STRIPE_SECRET=your-stripe-secret
@@ -124,7 +132,7 @@ Next, you should configure your Stripe keys in your `.env` file. You can retriev
### Currency Configuration
-The default Cashier currency is United States Dollars (USD). You can change the default currency by setting the `CASHIER_CURRENCY` environment variable:
+The default Cashier currency is United States Dollars (USD). You can change the default currency by setting the `CASHIER_CURRENCY` environment variable within your application's `.env` file:
CASHIER_CURRENCY=eur
@@ -135,9 +143,9 @@ In addition to configuring Cashier's currency, you may also specify a locale to
> {note} In order to use locales other than `en`, ensure the `ext-intl` PHP extension is installed and configured on your server.
-#### Logging
+### Logging
-Cashier allows you to specify the log channel to be used when logging all Stripe related exceptions. You may specify the log channel using the `CASHIER_LOGGER` environment variable:
+Cashier allows you to specify the log channel to be used when logging all Stripe related exceptions. You may specify the log channel by defining the `CASHIER_LOGGER` environment variable within your application's `.env` file:
CASHIER_LOGGER=stack
@@ -147,7 +155,7 @@ Cashier allows you to specify the log channel to be used when logging all Stripe
### Retrieving Customers
-You can retrieve a customer by their Stripe ID using the `Cashier::findBillable` method. This will return an instance of the Billable model:
+You can retrieve a customer by their Stripe ID using the `Cashier::findBillable` method. This method will return an instance of the billable model:
use Laravel\Cashier\Cashier;
@@ -160,51 +168,47 @@ Occasionally, you may wish to create a Stripe customer without beginning a subsc
$stripeCustomer = $user->createAsStripeCustomer();
-Once the customer has been created in Stripe, you may begin a subscription at a later date. You can also use an optional `$options` array to pass in any additional parameters which are supported by the Stripe API:
+Once the customer has been created in Stripe, you may begin a subscription at a later date. You may provide an optional `$options` array to pass in any additional [customer creation parameters that are supported by the Stripe API](https://stripe.com/docs/api/customers/create):
$stripeCustomer = $user->createAsStripeCustomer($options);
-You may use the `asStripeCustomer` method if you want to return the customer object if the billable entity is already a customer within Stripe:
+You may use the `asStripeCustomer` method if you want to return the Stripe customer object for a billable model:
$stripeCustomer = $user->asStripeCustomer();
-The `createOrGetStripeCustomer` method may be used if you want to return the customer object but are not sure whether the billable entity is already a customer within Stripe. This method will create a new customer in Stripe if one does not already exist:
+The `createOrGetStripeCustomer` method may be used if you would like to retrieve the Stripe customer object for a given billable model but are not sure whether the billable model is already a customer within Stripe. This method will create a new customer in Stripe if one does not already exist:
$stripeCustomer = $user->createOrGetStripeCustomer();
### Updating Customers
-Occasionally, you may wish to update the Stripe customer directly with additional information. You may accomplish this using the `updateStripeCustomer` method:
+Occasionally, you may wish to update the Stripe customer directly with additional information. You may accomplish this using the `updateStripeCustomer` method. This method accepts an array of [customer update options supported by the Stripe API](https://stripe.com/docs/api/customers/update):
$stripeCustomer = $user->updateStripeCustomer($options);
### Billing Portal
-Stripe offers [an easy way to set up a billing portal](https://stripe.com/docs/billing/subscriptions/customer-portal) so your customer can manage their subscription, payment methods, and view their billing history. You can redirect your users to the billing portal using the `redirectToBillingPortal` method from a controller or route:
+Stripe offers [an easy way to set up a billing portal](https://stripe.com/docs/billing/subscriptions/customer-portal) so that your customer can manage their subscription, payment methods, and view their billing history. You can redirect your users to the billing portal by invoking the `redirectToBillingPortal` method on the billable model from a controller or route:
use Illuminate\Http\Request;
- public function billingPortal(Request $request)
- {
+ Route::get('/billing-portal', function (Request $request) {
return $request->user()->redirectToBillingPortal();
- }
+ });
-By default, when the user is finished managing their subscription, they can return to the `home` route of your application. You may provide a custom URL the user should return to by passing the URL as an argument to the `redirectToBillingPortal` method:
+By default, when the user is finished managing their subscription, they will be able to return to the `home` route of your application via a link within the Stripe billing portal. You may provide a custom URL that the user should return to by passing the URL as an argument to the `redirectToBillingPortal` method:
use Illuminate\Http\Request;
- public function billingPortal(Request $request)
- {
- return $request->user()->redirectToBillingPortal(
- route('billing')
- );
- }
+ Route::get('/billing-portal', function (Request $request) {
+ return $request->user()->redirectToBillingPortal(route('billing'));
+ });
-If you would like to only generate the URL to the billing portal, you may use the `billingPortalUrl` method:
+If you would like to generate the URL to the billing portal without generating an HTTP redirect response, you may invoke the `billingPortalUrl` method:
- $url = $user->billingPortalUrl(route('billing'));
+ $url = $request->user()->billingPortalUrl(route('billing'));
## Payment Methods
@@ -212,12 +216,12 @@ If you would like to only generate the URL to the billing portal, you may use th
### Storing Payment Methods
-In order to create subscriptions or perform "one off" charges with Stripe, you will need to store a payment method and retrieve its identifier from Stripe. The approach used to accomplish differs based on whether you plan to use the payment method for subscriptions or single charges, so we will examine both below.
+In order to create subscriptions or perform "one off" charges with Stripe, you will need to store a payment method and retrieve its identifier from Stripe. The approach used to accomplish this differs based on whether you plan to use the payment method for subscriptions or single charges, so we will examine both below.
#### Payment Methods For Subscriptions
-When storing credit cards to a customer for future use, the Stripe Setup Intents API must be used to securely gather the customer's payment method details. A "Setup Intent" indicates to Stripe the intention to charge a customer's payment method. Cashier's `Billable` trait includes the `createSetupIntent` to easily create a new Setup Intent. You should call this method from the route or controller that will render the form which gathers your customer's payment method details:
+When storing a customer's credit card information for future use by a subscription, the Stripe "Setup Intents" API must be used to securely gather the customer's payment method details. A "Setup Intent" indicates to Stripe the intention to charge a customer's payment method. Cashier's `Billable` trait includes the `createSetupIntent` method to easily create a new Setup Intent. You should invoke this method from the route or controller that will render the form which gathers your customer's payment method details:
return view('update-payment-method', [
'intent' => $user->createSetupIntent()
@@ -225,50 +229,56 @@ When storing credit cards to a customer for future use, the Stripe Setup Intents
After you have created the Setup Intent and passed it to the view, you should attach its secret to the element that will gather the payment method. For example, consider this "update payment method" form:
-
+```html
+
-
-
+
+
-
+
+```
-Next, the Stripe.js library may be used to attach a Stripe Element to the form and securely gather the customer's payment details:
+Next, the Stripe.js library may be used to attach a [Stripe Element](https://stripe.com/docs/stripe-js) to the form and securely gather the customer's payment details:
-
+```html
+
-
+ cardElement.mount('#card-element');
+
+```
Next, the card can be verified and a secure "payment method identifier" can be retrieved from Stripe using [Stripe's `confirmCardSetup` method](https://stripe.com/docs/js/setup_intents/confirm_card_setup):
- const cardHolderName = document.getElementById('card-holder-name');
- const cardButton = document.getElementById('card-button');
- const clientSecret = cardButton.dataset.secret;
-
- cardButton.addEventListener('click', async (e) => {
- const { setupIntent, error } = await stripe.confirmCardSetup(
- clientSecret, {
- payment_method: {
- card: cardElement,
- billing_details: { name: cardHolderName.value }
- }
- }
- );
+```js
+const cardHolderName = document.getElementById('card-holder-name');
+const cardButton = document.getElementById('card-button');
+const clientSecret = cardButton.dataset.secret;
- if (error) {
- // Display "error.message" to the user...
- } else {
- // The card has been verified successfully...
+cardButton.addEventListener('click', async (e) => {
+ const { setupIntent, error } = await stripe.confirmCardSetup(
+ clientSecret, {
+ payment_method: {
+ card: cardElement,
+ billing_details: { name: cardHolderName.value }
+ }
}
- });
+ );
+
+ if (error) {
+ // Display "error.message" to the user...
+ } else {
+ // The card has been verified successfully...
+ }
+});
+```
After the card has been verified by Stripe, you may pass the resulting `setupIntent.payment_method` identifier to your Laravel application, where it can be attached to the customer. The payment method can either be [added as a new payment method](#adding-payment-methods) or [used to update the default payment method](#updating-the-default-payment-method). You can also immediately use the payment method identifier to [create a new subscription](#creating-subscriptions).
@@ -277,76 +287,82 @@ After the card has been verified by Stripe, you may pass the resulting `setupInt
#### Payment Methods For Single Charges
-Of course, when making a single charge against a customer's payment method we'll only need to use a payment method identifier a single time. Due to Stripe limitations, you may not use the stored default payment method of a customer for single charges. You must allow the customer to enter their payment method details using the Stripe.js library. For example, consider the following form:
+Of course, when making a single charge against a customer's payment method, we will only need to use a payment method identifier once. Due to Stripe limitations, you may not use the stored default payment method of a customer for single charges. You must allow the customer to enter their payment method details using the Stripe.js library. For example, consider the following form:
-
+```html
+
-
-
+
+
-
+
+```
-Next, the Stripe.js library may be used to attach a Stripe Element to the form and securely gather the customer's payment details:
+After defining such a form, the Stripe.js library may be used to attach a [Stripe Element](https://stripe.com/docs/stripe-js) to the form and securely gather the customer's payment details:
-
+```html
+
-
+ cardElement.mount('#card-element');
+
+```
Next, the card can be verified and a secure "payment method identifier" can be retrieved from Stripe using [Stripe's `createPaymentMethod` method](https://stripe.com/docs/stripe-js/reference#stripe-create-payment-method):
- const cardHolderName = document.getElementById('card-holder-name');
- const cardButton = document.getElementById('card-button');
-
- cardButton.addEventListener('click', async (e) => {
- const { paymentMethod, error } = await stripe.createPaymentMethod(
- 'card', cardElement, {
- billing_details: { name: cardHolderName.value }
- }
- );
+```js
+const cardHolderName = document.getElementById('card-holder-name');
+const cardButton = document.getElementById('card-button');
- if (error) {
- // Display "error.message" to the user...
- } else {
- // The card has been verified successfully...
+cardButton.addEventListener('click', async (e) => {
+ const { paymentMethod, error } = await stripe.createPaymentMethod(
+ 'card', cardElement, {
+ billing_details: { name: cardHolderName.value }
}
- });
+ );
+
+ if (error) {
+ // Display "error.message" to the user...
+ } else {
+ // The card has been verified successfully...
+ }
+});
+```
If the card is verified successfully, you may pass the `paymentMethod.id` to your Laravel application and process a [single charge](#simple-charge).
### Retrieving Payment Methods
-The `paymentMethods` method on the Billable model instance returns a collection of `Laravel\Cashier\PaymentMethod` instances:
+The `paymentMethods` method on the billable model instance returns a collection of `Laravel\Cashier\PaymentMethod` instances:
$paymentMethods = $user->paymentMethods();
-To retrieve the default payment method, the `defaultPaymentMethod` method may be used:
+To retrieve the customer's default payment method, the `defaultPaymentMethod` method may be used:
$paymentMethod = $user->defaultPaymentMethod();
-You can also retrieve a specific payment method that is owned by the Billable model using the `findPaymentMethod` method:
+You can retrieve a specific payment method that is attached to the billable model using the `findPaymentMethod` method:
$paymentMethod = $user->findPaymentMethod($paymentMethodId);
### Determining If A User Has A Payment Method
-To determine if a Billable model has a default payment method attached to their account, use the `hasDefaultPaymentMethod` method:
+To determine if a billable model has a default payment method attached to their account, invoke the `hasDefaultPaymentMethod` method:
if ($user->hasDefaultPaymentMethod()) {
//
}
-To determine if a Billable model has at least one payment method attached to their account, use the `hasPaymentMethod` method:
+You may use the `hasPaymentMethod` method to determine if a billable model has at least one payment method attached to their account:
if ($user->hasPaymentMethod()) {
//
@@ -363,12 +379,12 @@ To sync your default payment method information with the customer's default paym
$user->updateDefaultPaymentMethodFromStripe();
-> {note} The default payment method on a customer can only be used for invoicing and creating new subscriptions. Due to limitations from Stripe, it may not be used for single charges.
+> {note} The default payment method on a customer can only be used for invoicing and creating new subscriptions. Due to limitations imposed by Stripe, it may not be used for single charges.
### Adding Payment Methods
-To add a new payment method, you may call the `addPaymentMethod` method on the billable user, passing the payment method identifier:
+To add a new payment method, you may call the `addPaymentMethod` method on the billable model, passing the payment method identifier:
$user->addPaymentMethod($paymentMethod);
@@ -381,36 +397,42 @@ To delete a payment method, you may call the `delete` method on the `Laravel\Cas
$paymentMethod->delete();
-The `deletePaymentMethods` method will delete all of the payment method information for the Billable model:
+The `deletePaymentMethods` method will delete all of the payment method information for the billable model:
$user->deletePaymentMethods();
-> {note} If a user has an active subscription, you should prevent them from deleting their default payment method.
+> {note} If a user has an active subscription, your application should not allow them to delete their default payment method.
## Subscriptions
-Subscriptions provide a way to set up recurring payments for your customers and support multiple subscription plans, subscription quantities, trials, and more.
+Subscriptions provide a way to set up recurring payments for your customers. Stripe subscriptions managed by Cashier provide support for multiple subscription plans, subscription quantities, trials, and more.
### Creating Subscriptions
To create a subscription, first retrieve an instance of your billable model, which typically will be an instance of `App\Models\User`. Once you have retrieved the model instance, you may use the `newSubscription` method to create the model's subscription:
- $user = User::find(1);
+ use Illuminate\Http\Request;
+
+ Route::post('/user/subscribe', function (Request $request) {
+ $request->user()->newSubscription(
+ 'default', 'price_premium'
+ )->create($request->paymentMethodId);
- $user->newSubscription('default', 'price_premium')->create($paymentMethod);
+ // ...
+ });
The first argument passed to the `newSubscription` method should be the name of the subscription. If your application only offers a single subscription, you might call this `default` or `primary`. The second argument is the specific plan the user is subscribing to. This value should correspond to the plan's price identifier in Stripe.
-The `create` method, which accepts [a Stripe payment method identifier](#storing-payment-methods) or Stripe `PaymentMethod` object, will begin the subscription as well as update your database with the customer ID and other relevant billing information.
+The `create` method, which accepts [a Stripe payment method identifier](#storing-payment-methods) or Stripe `PaymentMethod` object, will begin the subscription as well as update your database with the billable model's Stripe customer ID and other relevant billing information.
-> {note} Passing a payment method identifier directly to the `create()` subscription method will also automatically add it to the user's stored payment methods.
+> {note} Passing a payment method identifier directly to the `create` subscription method will also automatically add it to the user's stored payment methods.
#### Quantities
-If you would like to set a specific quantity for the plan when creating the subscription, you may use the `quantity` method:
+If you would like to set a specific [quantity](https://stripe.com/docs/billing/subscriptions/quantities) for the plan when creating the subscription, you should invoke the `quantity` method on the subscription builder before creating the subscription:
$user->newSubscription('default', 'price_monthly')
->quantity(5)
@@ -419,7 +441,7 @@ If you would like to set a specific quantity for the plan when creating the subs
#### Additional Details
-If you would like to specify additional customer or subscription details, you may do so by passing them as the second and third arguments to the `create` method:
+If you would like to specify additional [customer](https://stripe.com/docs/api/customers/create) or [subscription](https://stripe.com/docs/api/subscriptions/create) options supported by Stripe, you may do so by passing them as the second and third arguments to the `create` method:
$user->newSubscription('default', 'price_monthly')->create($paymentMethod, [
'email' => $email,
@@ -427,8 +449,6 @@ If you would like to specify additional customer or subscription details, you ma
'metadata' => ['note' => 'Some extra information.'],
]);
-To learn more about the additional fields supported by Stripe, check out Stripe's documentation on [customer creation](https://stripe.com/docs/api#create_customer) and [subscription creation](https://stripe.com/docs/api/subscriptions/create).
-
#### Coupons
@@ -441,7 +461,9 @@ If you would like to apply a coupon when creating the subscription, you may use
#### Adding Subscriptions
-If you would like to add a subscription to a customer who already has a default payment method set you can use the `add` method when using the `newSubscription` method:
+If you would like to add a subscription to a customer who already has a default payment method you may invoke the `add` method on the subscription builder:
+
+ use App\Models\User;
$user = User::find(1);
@@ -450,7 +472,7 @@ If you would like to add a subscription to a customer who already has a default
### Checking Subscription Status
-Once a user is subscribed to your application, you may easily check their subscription status using a variety of convenient methods. First, the `subscribed` method returns `true` if the user has an active subscription, even if the subscription is currently within its trial period:
+Once a customer is subscribed to your application, you may easily check their subscription status using a variety of convenient methods. First, the `subscribed` method returns `true` if the customer has an active subscription, even if the subscription is currently within its trial period. The `subscribed` method accepts the name of the subscription as its first argument:
if ($user->subscribed('default')) {
//
@@ -458,29 +480,45 @@ Once a user is subscribed to your application, you may easily check their subscr
The `subscribed` method also makes a great candidate for a [route middleware](/docs/{{version}}/middleware), allowing you to filter access to routes and controllers based on the user's subscription status:
- public function handle($request, Closure $next)
+ user() && ! $request->user()->subscribed('default')) {
- // This user is not a paying customer...
- return redirect('billing');
- }
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @return mixed
+ */
+ public function handle($request, Closure $next)
+ {
+ if ($request->user() && ! $request->user()->subscribed('default')) {
+ // This user is not a paying customer...
+ return redirect('billing');
+ }
- return $next($request);
+ return $next($request);
+ }
}
-If you would like to determine if a user is still within their trial period, you may use the `onTrial` method. This method can be useful for displaying a warning to the user that they are still on their trial period:
+If you would like to determine if a user is still within their trial period, you may use the `onTrial` method. This method can be useful for determining if you should display a warning to the user that they are still on their trial period:
if ($user->subscription('default')->onTrial()) {
//
}
-The `subscribedToPlan` method may be used to determine if the user is subscribed to a given plan based on a given Stripe Price ID. In this example, we will determine if the user's `default` subscription is actively subscribed to the `monthly` plan:
+The `subscribedToPlan` method may be used to determine if the user is subscribed to a given plan based on a given Stripe plan's price identifier. In this example, we will determine if the user's `default` subscription is actively subscribed to the application's "monthly" plan. The given Stripe plan price identifier should correspond to one of your plan's price identifiers in the Stripe dashboard:
if ($user->subscribedToPlan('price_monthly', 'default')) {
//
}
-By passing an array to the `subscribedToPlan` method, you may determine if the user's `default` subscription is actively subscribed to the `monthly` or the `yearly` plan:
+By passing an array to the `subscribedToPlan` method, you may determine if the user's `default` subscription is actively subscribed to the application's "monthly" or "yearly" plan:
if ($user->subscribedToPlan(['price_monthly', 'price_yearly'], 'default')) {
//
@@ -497,13 +535,13 @@ The `recurring` method may be used to determine if the user is currently subscri
#### Cancelled Subscription Status
-To determine if the user was once an active subscriber, but has cancelled their subscription, you may use the `cancelled` method:
+To determine if the user was once an active subscriber but has cancelled their subscription, you may use the `cancelled` method:
if ($user->subscription('default')->cancelled()) {
//
}
-You may also determine if a user has cancelled their subscription, but are still on their "grace period" until the subscription fully expires. For example, if a user cancels a subscription on March 5th that was originally scheduled to expire on March 10th, the user is on their "grace period" until March 10th. Note that the `subscribed` method still returns `true` during this time:
+You may also determine if a user has cancelled their subscription but are still on their "grace period" until the subscription fully expires. For example, if a user cancels a subscription on March 5th that was originally scheduled to expire on March 10th, the user is on their "grace period" until March 10th. Note that the `subscribed` method still returns `true` during this time:
if ($user->subscription('default')->onGracePeriod()) {
//
@@ -515,37 +553,12 @@ To determine if the user has cancelled their subscription and is no longer withi
//
}
-
-#### Subscription Scopes
-
-Most subscription states are also available as query scopes so that you may easily query your database for subscriptions that are in a given state:
-
- // Get all active subscriptions...
- $subscriptions = Subscription::query()->active()->get();
-
- // Get all of the cancelled subscriptions for a user...
- $subscriptions = $user->subscriptions()->cancelled()->get();
-
-A complete list of available scopes is available below:
-
- Subscription::query()->active();
- Subscription::query()->cancelled();
- Subscription::query()->ended();
- Subscription::query()->incomplete();
- Subscription::query()->notCancelled();
- Subscription::query()->notOnGracePeriod();
- Subscription::query()->notOnTrial();
- Subscription::query()->onGracePeriod();
- Subscription::query()->onTrial();
- Subscription::query()->pastDue();
- Subscription::query()->recurring();
-
#### Incomplete and Past Due Status
If a subscription requires a secondary payment action after creation the subscription will be marked as `incomplete`. Subscription statuses are stored in the `stripe_status` column of Cashier's `subscriptions` database table.
-Similarly, if a secondary payment action is required when swapping plans the subscription will be marked as `past_due`. When your subscription is in either of these states it will not be active until the customer has confirmed their payment. Checking if a subscription has an incomplete payment can be done using the `hasIncompletePayment` method on the Billable model or a subscription instance:
+Similarly, if a secondary payment action is required when swapping plans the subscription will be marked as `past_due`. When your subscription is in either of these states it will not be active until the customer has confirmed their payment. Determining if a subscription has an incomplete payment may be accomplished using the `hasIncompletePayment` method on the billable model or a subscription instance:
if ($user->hasIncompletePayment('default')) {
//
@@ -557,11 +570,13 @@ Similarly, if a secondary payment action is required when swapping plans the sub
When a subscription has an incomplete payment, you should direct the user to Cashier's payment confirmation page, passing the `latestPayment` identifier. You may use the `latestPayment` method available on subscription instance to retrieve this identifier:
-
- Please confirm your payment.
-
+```html
+
+ Please confirm your payment.
+
+```
-If you would like the subscription to still be considered active when it's in a `past_due` state, you may use the `keepPastDueSubscriptionsActive` method provided by Cashier. Typically, this method should be called in the `register` method of your `AppServiceProvider`:
+If you would like the subscription to still be considered active when it's in a `past_due` state, you may use the `keepPastDueSubscriptionsActive` method provided by Cashier. Typically, this method should be called in the `register` method of your `App\Providers\AppServiceProvider`:
use Laravel\Cashier\Cashier;
@@ -577,35 +592,62 @@ If you would like the subscription to still be considered active when it's in a
> {note} When a subscription is in an `incomplete` state it cannot be changed until the payment is confirmed. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in an `incomplete` state.
+
+#### Subscription Scopes
+
+Most subscription states are also available as query scopes so that you may easily query your database for subscriptions that are in a given state:
+
+ // Get all active subscriptions...
+ $subscriptions = Subscription::query()->active()->get();
+
+ // Get all of the cancelled subscriptions for a user...
+ $subscriptions = $user->subscriptions()->cancelled()->get();
+
+A complete list of available scopes is available below:
+
+ Subscription::query()->active();
+ Subscription::query()->cancelled();
+ Subscription::query()->ended();
+ Subscription::query()->incomplete();
+ Subscription::query()->notCancelled();
+ Subscription::query()->notOnGracePeriod();
+ Subscription::query()->notOnTrial();
+ Subscription::query()->onGracePeriod();
+ Subscription::query()->onTrial();
+ Subscription::query()->pastDue();
+ Subscription::query()->recurring();
+
### Changing Plans
-After a user is subscribed to your application, they may occasionally want to change to a new subscription plan. To swap a user to a new subscription, pass the plan's price identifier to the `swap` method:
+After a customer is subscribed to your application, they may occasionally want to change to a new subscription plan. To swap a customer to a new plan, pass the Stripe plan's price identifier to the `swap` method. The given price identifier should correspond to a Stripe plan price identifier available in the Stripe dashboard:
+
+ use App\Models\User;
$user = App\Models\User::find(1);
- $user->subscription('default')->swap('provider-price-id');
+ $user->subscription('default')->swap('price_id');
-If the user is on trial, the trial period will be maintained. Also, if a "quantity" exists for the subscription, that quantity will also be maintained.
+If the customer is on trial, the trial period will be maintained. Also, if a "quantity" exists for the subscription, that quantity will also be maintained.
-If you would like to swap plans and cancel any trial period the user is currently on, you may use the `skipTrial` method:
+If you would like to swap plans and cancel any trial period the customer is currently on, you may invoke the `skipTrial` method:
$user->subscription('default')
->skipTrial()
- ->swap('provider-price-id');
+ ->swap('price_id');
-If you would like to swap plans and immediately invoice the user instead of waiting for their next billing cycle, you may use the `swapAndInvoice` method:
+If you would like to swap plans and immediately invoice the customer instead of waiting for their next billing cycle, you may use the `swapAndInvoice` method:
- $user = App\Models\User::find(1);
+ $user = User::find(1);
- $user->subscription('default')->swapAndInvoice('provider-price-id');
+ $user->subscription('default')->swapAndInvoice('price_id');
#### Prorations
-By default, Stripe prorates charges when swapping between plans. The `noProrate` method may be used to update the subscription's without prorating the charges:
+By default, Stripe prorates charges when swapping between plans. The `noProrate` method may be used to update the subscription's plan without prorating the charges:
- $user->subscription('default')->noProrate()->swap('provider-price-id');
+ $user->subscription('default')->noProrate()->swap('price_id');
For more information on subscription proration, consult the [Stripe documentation](https://stripe.com/docs/billing/subscriptions/prorations).
@@ -614,7 +656,9 @@ For more information on subscription proration, consult the [Stripe documentatio
### Subscription Quantity
-Sometimes subscriptions are affected by "quantity". For example, your application might charge $10 per month **per user** on an account. To easily increment or decrement your subscription quantity, use the `incrementQuantity` and `decrementQuantity` methods:
+Sometimes subscriptions are affected by "quantity". For example, a project management application might charge $10 per month per project. You may use the `incrementQuantity` and `decrementQuantity` methods to easily increment or decrement your subscription quantity:
+
+ use App\Models\User;
$user = User::find(1);
@@ -625,7 +669,7 @@ Sometimes subscriptions are affected by "quantity". For example, your applicatio
$user->subscription('default')->decrementQuantity();
- // Subtract five to the subscription's current quantity...
+ // Subtract five from the subscription's current quantity...
$user->subscription('default')->decrementQuantity(5);
Alternatively, you may set a specific quantity using the `updateQuantity` method:
@@ -638,37 +682,40 @@ The `noProrate` method may be used to update the subscription's quantity without
For more information on subscription quantities, consult the [Stripe documentation](https://stripe.com/docs/subscriptions/quantities).
-> {note} Please note that when working with multiplan subscriptions, an extra "plan" parameter is required for the above quantity methods.
+
+#### Multiplan Subscription Quantities
+
+If your subscription is a [multiplan subscription](#multiplan-subscriptions), you should pass the name of the plan whose quantity you wish to increment or decrement as the second argument to the increment / decrement methods:
+
+ $user->subscription('default')->incrementQuantity(1, 'chat-plan');
### Multiplan Subscriptions
-[Multiplan subscriptions](https://stripe.com/docs/billing/subscriptions/multiplan) allow you to assign multiple billing plans to a single subscription. For example, imagine you are building a customer service "helpdesk" application that has a base subscription of $10 per month, but offers a live chat add-on plan for an additional $15 per month:
-
- $user = User::find(1);
+[Multiplan subscriptions](https://stripe.com/docs/billing/subscriptions/multiplan) allow you to assign multiple billing plans to a single subscription. For example, imagine you are building a customer service "helpdesk" application that has a base subscription plan of $10 per month but offers a live chat add-on plan for an additional $15 per month. Multiplan subscription information is stored in Cashier's `subscription_items` database table.
- $user->newSubscription('default', [
- 'price_monthly',
- 'chat-plan',
- ])->create($paymentMethod);
+You may specify multiple plans for a given subscription by passing an array of plans as the second argument to the `newSubscription` method:
-Now the customer will have two plans on their `default` subscription. Both plans will be charged for on their respective billing intervals. You can also use the `quantity` method to indicate the specific quantity for each plan:
+ use Illuminate\Http\Request;
- $user = User::find(1);
+ Route::post('/user/subscribe', function (Request $request) {
+ $request->user()->newSubscription('default', [
+ 'price_monthly',
+ 'chat-plan',
+ ])->create($request->paymentMethodId);
- $user->newSubscription('default', ['price_monthly', 'chat-plan'])
- ->quantity(5, 'chat-plan')
- ->create($paymentMethod);
+ // ...
+ });
-Or, you may dynamically add the extra plan and its quantity using the `plan` method:
+In the example above, the customer will have two plans attached to their `default` subscription. Both plans will be charged on their respective billing intervals. If necessary, you may use the `quantity` method to indicate a specific quantity for each plan:
$user = User::find(1);
- $user->newSubscription('default', 'price_monthly')
- ->plan('chat-plan', 5)
+ $user->newSubscription('default', ['price_monthly', 'chat-plan'])
+ ->quantity(5, 'chat-plan')
->create($paymentMethod);
-Alternatively, you may add a new plan to an existing subscription at a later time:
+If you would like to add another plan to an existing subscription, you may invoke the subscription's `addPlan` method:
$user = User::find(1);
@@ -678,7 +725,7 @@ The example above will add the new plan and the customer will be billed for it o
$user->subscription('default')->addPlanAndInvoice('chat-plan');
-If you would like to add a plan with a specific quantity, you can pass the quantity as the second parameter of the `addPlan` or `addPlanAndInvoice` methods:
+If you would like to add a plan with a specific quantity, you can pass the quantity as the second argument of the `addPlan` or `addPlanAndInvoice` methods:
$user = User::find(1);
@@ -690,18 +737,20 @@ You may remove plans from subscriptions using the `removePlan` method:
> {note} You may not remove the last plan on a subscription. Instead, you should simply cancel the subscription.
-
-### Swapping
+
+#### Swapping Plans
-You may also change the plans attached to a multiplan subscription. For example, imagine you're on a `basic-plan` subscription with a `chat-plan` add-on and you want to upgrade to the `pro-plan` plan:
+You may also change the plans attached to a multiplan subscription. For example, imagine a customer has a `basic-plan` subscription with a `chat-plan` add-on plan and you want to upgrade the customer from the `basic-plan` to the `pro-plan` plan:
+
+ use App\Models\User;
$user = User::find(1);
$user->subscription('default')->swap(['pro-plan', 'chat-plan']);
-When executing the code above, the underlying subscription item with the `basic-plan` is deleted and the one with the `chat-plan` is preserved. Additionally, a new subscription item for the new `pro-plan` is created.
+When executing the example above, the underlying subscription item with the `basic-plan` is deleted and the one with the `chat-plan` is preserved. Additionally, a new subscription item for the `pro-plan` is created.
-You can also specify subscription item options. For example, you may need to specify the subscription plan quantities:
+You can also specify subscription item options by passing an array of key / value pairs to the `swap` method. For example, you may need to specify the subscription plan quantities:
$user = User::find(1);
@@ -710,7 +759,7 @@ You can also specify subscription item options. For example, you may need to spe
'chat-plan'
]);
-If you want to swap a single plan on a subscription, you may do so using the `swap` method on the subscription item itself. This approach is useful if you, for example, want to preserve all of the existing metadata on the subscription item.
+If you want to swap a single plan on a subscription, you may do so using the `swap` method on the subscription item itself. This approach is particularly useful if you would like to preserve all of the existing metadata on the subscription's other plans:
$user = User::find(1);
@@ -721,14 +770,14 @@ If you want to swap a single plan on a subscription, you may do so using the `sw
#### Proration
-By default, Stripe will prorate charges when adding or removing plans from a subscription. If you would like to make a plan adjustment without proration, you should chain the `noProrate` method onto your plan operation:
+By default, Stripe will prorate charges when adding or removing plans from a multiplan subscription. If you would like to make a plan adjustment without proration, you should chain the `noProrate` method onto your plan operation:
$user->subscription('default')->noProrate()->removePlan('chat-plan');
#### Quantities
-If you would like to update quantities on individual subscription plans, you may do so using the [existing quantity methods](#subscription-quantity) and passing the name of the plan as an additional argument to the method:
+If you would like to update quantities on individual subscription plans, you may do so using the [existing quantity methods](#subscription-quantity) by passing the name of the plan as an additional argument to the method:
$user = User::find(1);
@@ -738,13 +787,15 @@ If you would like to update quantities on individual subscription plans, you may
$user->subscription('default')->updateQuantity(10, 'chat-plan');
-> {note} When you have multiple plans set on a subscription the `stripe_plan` and `quantity` attributes on the `Subscription` model will be `null`. To access the individual plans, you should use the `items` relationship available on the `Subscription` model.
+> {note} When a subscription has multiple plans the `stripe_plan` and `quantity` attributes on the `Subscription` model will be `null`. To access the individual plan attributes, you should use the `items` relationship available on the `Subscription` model.
#### Subscription Items
When a subscription has multiple plans, it will have multiple subscription "items" stored in your database's `subscription_items` table. You may access these via the `items` relationship on the subscription:
+ use App\Models\User;
+
$user = User::find(1);
$subscriptionItem = $user->subscription('default')->items->first();
@@ -762,15 +813,27 @@ You can also retrieve a specific plan using the `findItemOrFail` method:
### Subscription Taxes
-To specify the tax rates a user pays on a subscription, implement the `taxRates` method on your billable model, and return an array with the Tax Rate IDs. You can define these tax rates in [your Stripe dashboard](https://dashboard.stripe.com/test/tax-rates):
+To specify the tax rates a user pays on a subscription, you should implement the `taxRates` method on your billable model and return an array containing the Stripe tax rate IDs. You can define these tax rates in [your Stripe dashboard](https://dashboard.stripe.com/test/tax-rates):
+ /**
+ * The tax rates that should apply to the customer's subscriptions.
+ *
+ * @return array
+ */
public function taxRates()
{
return ['tax-rate-id'];
}
-The `taxRates` method enables you to apply a tax rate on a model-by-model basis, which may be helpful for a user base that spans multiple countries and tax rates. If you're working with multiplan subscriptions you can define different tax rates for each plan by implementing a `planTaxRates` method on your billable model:
+The `taxRates` method enables you to apply a tax rate on a customer-by-customer basis, which may be helpful for a user base that spans multiple countries and tax rates.
+
+If you're offering multiplan subscriptions, you may define different tax rates for each plan by implementing a `planTaxRates` method on your billable model:
+ /**
+ * The tax rates that should apply to the customer's subscriptions.
+ *
+ * @return array
+ */
public function planTaxRates()
{
return [
@@ -783,16 +846,18 @@ The `taxRates` method enables you to apply a tax rate on a model-by-model basis,
#### Syncing Tax Rates
-When changing the hard-coded Tax Rate IDs returned by the `taxRates` method, the tax settings on any existing subscriptions for the user will remain the same. If you wish to update the tax value for existing subscriptions with the returned `taxTaxRates` values, you should call the `syncTaxRates` method on the user's subscription instance:
+When changing the hard-coded tax rate IDs returned by the `taxRates` method, the tax settings on any existing subscriptions for the user will remain the same. If you wish to update the tax value for existing subscriptions with the new `taxRates` values, you should call the `syncTaxRates` method on the user's subscription instance:
$user->subscription('default')->syncTaxRates();
-This will also sync any subscription item tax rates so make sure you also properly change the `planTaxRates` method.
+This will also sync any multiplan subscription item tax rates. If your application is offering multiplan subscriptions, you should ensure that your billable model implements the `planTaxRates` method [discussed above](#subscription-taxes).
#### Tax Exemption
-Cashier also offers methods to determine if the customer is tax exempt by calling the Stripe API. The `isNotTaxExempt`, `isTaxExempt`, and `reverseChargeApplies` methods are available on the billable model:
+Cashier also offers the `isNotTaxExempt`, `isTaxExempt`, and `reverseChargeApplies` methods to determine if the customer is tax exempt. These methods will call the Stripe API to determine a customer's tax exemption status:
+
+ use App\Models\User;
$user = User::find(1);
@@ -800,23 +865,24 @@ Cashier also offers methods to determine if the customer is tax exempt by callin
$user->isNotTaxExempt();
$user->reverseChargeApplies();
-These methods are also available on any `Laravel\Cashier\Invoice` object. However, when calling these methods on an `Invoice` object the methods will determine the exemption status at the time the invoice was created.
+> {note} These methods are also available on any `Laravel\Cashier\Invoice` object. However, when invoked on an `Invoice` object, the methods will determine the exemption status at the time the invoice was created.
### Subscription Anchor Date
-By default, the billing cycle anchor is the date the subscription was created, or if a trial period is used, the date that the trial ends. If you would like to modify the billing anchor date, you may use the `anchorBillingCycleOn` method:
+By default, the billing cycle anchor is the date the subscription was created or, if a trial period is used, the date that the trial ends. If you would like to modify the billing anchor date, you may use the `anchorBillingCycleOn` method:
- use App\Models\User;
- use Carbon\Carbon;
+ use Illuminate\Http\Request;
- $user = User::find(1);
+ Route::post('/user/subscribe', function (Request $request) {
+ $anchor = Carbon::parse('first day of next month');
- $anchor = Carbon::parse('first day of next month');
+ $request->user()->newSubscription('default', 'price_premium')
+ ->anchorBillingCycleOn($anchor->startOfDay())
+ ->create($request->paymentMethodId);
- $user->newSubscription('default', 'price_premium')
- ->anchorBillingCycleOn($anchor->startOfDay())
- ->create($paymentMethod);
+ // ...
+ });
For more information on managing subscription billing cycles, consult the [Stripe billing cycle documentation](https://stripe.com/docs/billing/subscriptions/billing-cycle)
@@ -827,7 +893,9 @@ To cancel a subscription, call the `cancel` method on the user's subscription:
$user->subscription('default')->cancel();
-When a subscription is cancelled, Cashier will automatically set the `ends_at` column in your database. This column is used to know when the `subscribed` method should begin returning `false`. For example, if a customer cancels a subscription on March 1st, but the subscription was not scheduled to end until March 5th, the `subscribed` method will continue to return `true` until March 5th.
+When a subscription is cancelled, Cashier will automatically set the `ends_at` column in your `subscriptions` database table. This column is used to know when the `subscribed` method should begin returning `false`.
+
+For example, if a customer cancels a subscription on March 1st, but the subscription was not scheduled to end until March 5th, the `subscribed` method will continue to return `true` until March 5th. This is done because a user is typically allowed to continue using an application until the end of their billing cycle.
You may determine if a user has cancelled their subscription but are still on their "grace period" using the `onGracePeriod` method:
@@ -842,11 +910,11 @@ If you wish to cancel a subscription immediately, call the `cancelNow` method on
### Resuming Subscriptions
-If a user has cancelled their subscription and you wish to resume it, use the `resume` method. The user **must** still be on their grace period in order to resume a subscription:
+If a customer has cancelled their subscription and you wish to resume it, you may invoke the `resume` method on the subscription. The customer must still be within their "grace period" in order to resume a subscription:
$user->subscription('default')->resume();
-If the user cancels a subscription and then resumes that subscription before the subscription has fully expired, they will not be billed immediately. Instead, their subscription will be re-activated, and they will be billed on the original billing cycle.
+If the customer cancels a subscription and then resumes that subscription before the subscription has fully expired the customer will not be billed immediately. Instead, their subscription will be re-activated and they will be billed on the original billing cycle.
## Subscription Trials
@@ -856,17 +924,21 @@ If the user cancels a subscription and then resumes that subscription before the
If you would like to offer trial periods to your customers while still collecting payment method information up front, you should use the `trialDays` method when creating your subscriptions:
- $user = User::find(1);
+ use Illuminate\Http\Request;
- $user->newSubscription('default', 'price_monthly')
- ->trialDays(10)
- ->create($paymentMethod);
+ Route::post('/user/subscribe', function (Request $request) {
+ $request->user()->newSubscription('default', 'price_monthly')
+ ->trialDays(10)
+ ->create($request->paymentMethodId);
-This method will set the trial period ending date on the subscription record within the database, as well as instruct Stripe to not begin billing the customer until after this date. When using the `trialDays` method, Cashier will overwrite any default trial period configured for the plan in Stripe.
+ // ...
+ });
+
+This method will set the trial period ending date on the subscription record within the database and instruct Stripe to not begin billing the customer until after this date. When using the `trialDays` method, Cashier will overwrite any default trial period configured for the plan in Stripe.
> {note} If the customer's subscription is not cancelled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date.
-The `trialUntil` method allows you to provide a `DateTime` instance to specify when the trial period should end:
+The `trialUntil` method allows you to provide a `DateTime` instance that specifies when the trial period should end:
use Carbon\Carbon;
@@ -874,7 +946,7 @@ The `trialUntil` method allows you to provide a `DateTime` instance to specify w
->trialUntil(Carbon::now()->addDays(10))
->create($paymentMethod);
-You may determine if the user is within their trial period using either the `onTrial` method of the user instance, or the `onTrial` method of the subscription instance. The two examples below are identical:
+You may determine if a user is within their trial period using either the `onTrial` method of the user instance or the `onTrial` method of the subscription instance. The two examples below are equivalent:
if ($user->onTrial('default')) {
//
@@ -894,14 +966,16 @@ You may choose to define how many trial days your plan's receive in the Stripe d
If you would like to offer trial periods without collecting the user's payment method information up front, you may set the `trial_ends_at` column on the user record to your desired trial ending date. This is typically done during user registration:
+ use App\Models\User;
+
$user = User::create([
- // Populate other user properties...
+ // ...
'trial_ends_at' => now()->addDays(10),
]);
-> {note} Be sure to add a [date mutator](/docs/{{version}}/eloquent-mutators#date-mutators) for `trial_ends_at` to your model definition.
+> {note} Be sure to add a [date cast](/docs/{{version}}/eloquent-mutators##date-casting) for the `trial_ends_at` attribute within your billable model's class definition.
-Cashier refers to this type of trial as a "generic trial", since it is not attached to any existing subscription. The `onTrial` method on the `User` instance will return `true` if the current date is not past the value of `trial_ends_at`:
+Cashier refers to this type of trial as a "generic trial", since it is not attached to any existing subscription. The `onTrial` method on the billable model instance will return `true` if the current date is not past the value of `trial_ends_at`:
if ($user->onTrial()) {
// User is within their trial period...
@@ -919,7 +993,7 @@ To retrieve the user's trial ending date, you may use the `trialEndsAt` method.
$trialEndsAt = $user->trialEndsAt('main');
}
-You may also use the `onGenericTrial` method if you wish to know specifically that the user is within their "generic" trial period and has not created an actual subscription yet:
+You may also use the `onGenericTrial` method if you wish to know specifically that the user is within their "generic" trial period and has not yet created an actual subscription:
if ($user->onGenericTrial()) {
// User is within their "generic" trial period...
@@ -928,7 +1002,11 @@ You may also use the `onGenericTrial` method if you wish to know specifically th
### Extending Trials
-The `extendTrial` method allows you to extend the trial period of a subscription after it's been created:
+The `extendTrial` method allows you to extend the trial period of a subscription after the subscription has been created. If the trial has already expired and the customer is already being billed for the subscription, you can still offer them an extended trial. The time spent within the trial period will be deducted from the customer's next invoice:
+
+ use App\Models\User;
+
+ $subscription = User::find(1)->subscription('default');
// End the trial 7 days from now...
$subscription->extendTrial(
@@ -940,18 +1018,16 @@ The `extendTrial` method allows you to extend the trial period of a subscription
$subscription->trial_ends_at->addDays(5)
);
-If the trial has already expired and the customer is already being billed for the subscription, you can still offer them an extended trial. The time spent within the trial period will be deducted from the customer's next invoice.
-
## Handling Stripe Webhooks
> {tip} You may use [the Stripe CLI](https://stripe.com/docs/stripe-cli) to help test webhooks during local development.
-Stripe can notify your application of a variety of events via webhooks. By default, a route that points to Cashier's webhook controller is configured through the Cashier service provider. This controller will handle all incoming webhook requests.
+Stripe can notify your application of a variety of events via webhooks. By default, a route that points to Cashier's webhook controller is automatically registered by the Cashier service provider. This controller will handle all incoming webhook requests.
-By default, this controller will automatically handle cancelling subscriptions that have too many failed charges (as defined by your Stripe settings), customer updates, customer deletions, subscription updates, and payment method changes; however, as we'll soon discover, you can extend this controller to handle any webhook event you like.
+By default, the Cashier webhook controller will automatically handle cancelling subscriptions that have too many failed charges (as defined by your Stripe settings), customer updates, customer deletions, subscription updates, and payment method changes; however, as we'll soon discover, you can extend this controller to handle any Stripe webhook event you like.
-To ensure your application can handle Stripe webhooks, be sure to configure the webhook URL in the Stripe control panel. By default, Cashier's webhook controller listens to the `/stripe/webhook` URL path. The full list of all webhooks you should configure in the Stripe control panel are:
+To ensure your application can handle Stripe webhooks, be sure to configure the webhook URL in the Stripe control panel. By default, Cashier's webhook controller responds to the `/stripe/webhook` URL path. The full list of all webhooks you should enable in the Stripe control panel are:
- `customer.subscription.updated`
- `customer.subscription.deleted`
@@ -959,12 +1035,12 @@ To ensure your application can handle Stripe webhooks, be sure to configure the
- `customer.deleted`
- `invoice.payment_action_required`
-> {note} Make sure you protect incoming requests with Cashier's included [webhook signature verification](/docs/{{version}}/billing#verifying-webhook-signatures) middleware.
+> {note} Make sure you protect incoming Stripe webhook requests with Cashier's included [webhook signature verification](/docs/{{version}}/billing#verifying-webhook-signatures) middleware.
#### Webhooks & CSRF Protection
-Since Stripe webhooks need to bypass Laravel's [CSRF protection](/docs/{{version}}/csrf), be sure to list the URI as an exception in your `VerifyCsrfToken` middleware or list the route outside of the `web` middleware group:
+Since Stripe webhooks need to bypass Laravel's [CSRF protection](/docs/{{version}}/csrf), be sure to list the URI as an exception in your application's `App\Http\Middleware\VerifyCsrfToken` middleware or list the route outside of the `web` middleware group:
protected $except = [
'stripe/*',
@@ -973,7 +1049,9 @@ Since Stripe webhooks need to bypass Laravel's [CSRF protection](/docs/{{version
### Defining Webhook Event Handlers
-Cashier automatically handles subscription cancellation on failed charges, but if you have additional webhook events you would like to handle, extend the Webhook controller. Your method names should correspond to Cashier's expected convention, specifically, methods should be prefixed with `handle` and the "camel case" name of the webhook you wish to handle. For example, if you wish to handle the `invoice.payment_succeeded` webhook, you should add a `handleInvoicePaymentSucceeded` method to the controller:
+Cashier automatically handles subscription cancellations for failed charges and other common Stripe webhook events. However, if you have additional webhook events you would like to handle, you may do so by extending the Cashier webhook controller.
+
+Your controller's method names should correspond to Cashier's controller conventions. Specifically, methods should be prefixed with `handle` and the "camel case" name of the webhook you wish to handle. For example, if you wish to handle the `invoice.payment_succeeded` webhook, you should add a `handleInvoicePaymentSucceeded` method to the controller:
-### Failed Subscriptions
-
-What if a customer's credit card expires? No worries - Cashier's Webhook controller will cancel the customer's subscription for you. Failed payments will automatically be captured and handled by the controller. The controller will cancel the customer's subscription when Stripe determines the subscription has failed (normally after three failed payment attempts).
+> {tip} Cashier emits a `Laravel\Cashier\Events\WebhookReceived` event when a webhook is received and a `Laravel\Cashier\Events\WebhookHandled` event when a webhook was handled by Cashier. Both events contain the full payload of the Stripe webhook.
### Verifying Webhook Signatures
To secure your webhooks, you may use [Stripe's webhook signatures](https://stripe.com/docs/webhooks/signatures). For convenience, Cashier automatically includes a middleware which validates that the incoming Stripe webhook request is valid.
-To enable webhook verification, ensure that the `STRIPE_WEBHOOK_SECRET` environment variable is set in your `.env` file. The webhook `secret` may be retrieved from your Stripe account dashboard.
+To enable webhook verification, ensure that the `STRIPE_WEBHOOK_SECRET` environment variable is set in your application's `.env` file. The webhook `secret` may be retrieved from your Stripe account dashboard.
## Single Charges
@@ -1024,20 +1097,27 @@ To enable webhook verification, ensure that the `STRIPE_WEBHOOK_SECRET` environm
### Simple Charge
-> {note} The `charge` method accepts the amount you would like to charge in the **lowest denominator of the currency used by your application**.
+> {note} The `charge` method accepts the amount you would like to charge in the lowest denominator of the currency used by your application. For example, when using United States Dollars, amounts should be specified in pennies.
-If you would like to make a "one off" charge against a subscribed customer's payment method, you may use the `charge` method on a billable model instance. You'll need to [provide a payment method identifier](#storing-payment-methods) as the second argument:
+If you would like to make a one-time charge against a customer, you may use the `charge` method on a billable model instance. You will need to [provide a payment method identifier](#payment-methods-for-single-charges) as the second argument to the `charge` method:
- // Stripe Accepts Charges In Cents...
- $stripeCharge = $user->charge(100, $paymentMethod);
+ use Illuminate\Http\Request;
+
+ Route::post('/purchase', function (Request $request) {
+ $stripeCharge = $request->user()->charge(
+ 100, $request->paymentMethodId
+ );
+
+ // ...
+ });
-The `charge` method accepts an array as its third argument, allowing you to pass any options you wish to the underlying Stripe charge creation. Consult the Stripe documentation regarding the options available to you when creating charges:
+The `charge` method accepts an array as its third argument, allowing you to pass any options you wish to the underlying Stripe charge creation. More information regarding the options available to you when creating charges may be found in the [Stripe documentation](https://stripe.com/docs/api/charges/create):
$user->charge(100, $paymentMethod, [
'custom_option' => $value,
]);
-You may also use the `charge` method without an underlying customer or user:
+You may also use the `charge` method without an underlying customer or user. To accomplish this, invoke the `charge` method on an new instance of your application's billable model:
use App\Models\User;
@@ -1054,12 +1134,11 @@ The `charge` method will throw an exception if the charge fails. If the charge i
### Charge With Invoice
-Sometimes you may need to make a one-time charge but also generate an invoice for the charge so that you may offer a PDF receipt to your customer. The `invoiceFor` method lets you do just that. For example, let's invoice the customer $5.00 for a "One Time Fee":
+Sometimes you may need to make a one-time charge and offer a PDF receipt to your customer. The `invoiceFor` method lets you do just that. For example, let's invoice a customer $5.00 for a "Maintenance Fee":
- // Stripe Accepts Charges In Cents...
$user->invoiceFor('One Time Fee', 500);
-The invoice will be charged immediately against the user's default payment method. The `invoiceFor` method also accepts an array as its third argument. This array contains the billing options for the invoice item. The fourth argument accepted by the method is also an array. This final argument accepts the billing options for the invoice itself:
+The invoice will be charged immediately against the user's default payment method. The `invoiceFor` method also accepts an array as its third argument. This array contains the billing options for the invoice item. The fourth argument accepted by the method is also an array which should contain the billing options for the invoice itself:
$user->invoiceFor('Stickers', 500, [
'quantity' => 50,
@@ -1072,9 +1151,9 @@ The invoice will be charged immediately against the user's default payment metho
### Refunding Charges
-If you need to refund a Stripe charge, you may use the `refund` method. This method accepts the Stripe Payment Intent ID as its first argument:
+If you need to refund a Stripe charge, you may use the `refund` method. This method accepts the Stripe [payment intent ID](#payment-methods-for-single-charges) as its first argument:
- $payment = $user->charge(100, $paymentMethod);
+ $payment = $user->charge(100, $paymentMethodId);
$user->refund($payment->id);
@@ -1084,21 +1163,22 @@ If you need to refund a Stripe charge, you may use the `refund` method. This met
### Retrieving Invoices
-You may easily retrieve an array of a billable model's invoices using the `invoices` method:
+You may easily retrieve an array of a billable model's invoices using the `invoices` method. The `invoices` method returns a collection of `Laravel\Cashier\Invoice` instances:
$invoices = $user->invoices();
- // Include pending invoices in the results...
+If you would like to include pending invoices in the results, you may use the `invoicesIncludingPending` method:
+
$invoices = $user->invoicesIncludingPending();
-You may use the `findInvoice` method to retrieve a specific invoice:
+You may use the `findInvoice` method to retrieve a specific invoice by its ID:
$invoice = $user->findInvoice($invoiceId);
#### Displaying Invoice Information
-When listing the invoices for the customer, you may use the invoice's helper methods to display the relevant invoice information. For example, you may wish to list every invoice in a table, allowing the user to easily download any of them:
+When listing the invoices for the customer, you may use the invoice's methods to display the relevant invoice information. For example, you may wish to list every invoice in a table, allowing the user to easily download any of them:
@foreach ($invoices as $invoice)
@@ -1113,18 +1193,18 @@ When listing the invoices for the customer, you may use the invoice's helper met
### Generating Invoice PDFs
-From within a route or controller, use the `downloadInvoice` method to generate a PDF download of the invoice. This method will automatically generate the proper HTTP response to send the download to the browser:
+From within a route or controller, you may use the `downloadInvoice` method to generate a PDF download of a given invoice. This method will automatically generate the proper HTTP response needed to download the invoice:
use Illuminate\Http\Request;
- Route::get('user/invoice/{invoice}', function (Request $request, $invoiceId) {
+ Route::get('/user/invoice/{invoice}', function (Request $request, $invoiceId) {
return $request->user()->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
'product' => 'Your Product',
]);
});
-The `downloadInvoice` method also allows for an optional custom filename as the third parameter. This filename will automatically be suffixed with `.pdf` for you:
+The `downloadInvoice` method also allows for a custom filename via its third argument. This filename will automatically be suffixed with `.pdf` for you:
return $request->user()->downloadInvoice($invoiceId, [
'vendor' => 'Your Company',
@@ -1134,9 +1214,9 @@ The `downloadInvoice` method also allows for an optional custom filename as the
## Handling Failed Payments
-Sometimes, payments for subscriptions or single charges can fail. When this happens, Cashier will throw an `IncompletePayment` exception that informs you that this happened. After catching this exception, you have two options on how to proceed.
+Sometimes, payments for subscriptions or single charges can fail. When this happens, Cashier will throw an `Laravel\Cashier\Exceptions\IncompletePayment` exception that informs you that this happened. After catching this exception, you have two options on how to proceed.
-First, you could redirect your customer to the dedicated payment confirmation page which is included with Cashier. This page already has an associated route that is registered via Cashier's service provider. So, you may catch the `IncompletePayment` exception and redirect to the payment confirmation page:
+First, you could redirect your customer to the dedicated payment confirmation page which is included with Cashier. This page already has an associated named route that is registered via Cashier's service provider. So, you may catch the `IncompletePayment` exception and redirect the user to the payment confirmation page:
use Laravel\Cashier\Exceptions\IncompletePayment;
@@ -1150,42 +1230,52 @@ First, you could redirect your customer to the dedicated payment confirmation pa
);
}
-On the payment confirmation page, the customer will be prompted to enter their credit card info again and perform any additional actions required by Stripe, such as "3D Secure" confirmation. After confirming their payment, the user will be redirected to the URL provided by the `redirect` parameter specified above. Upon redirection, `message` (string) and `success` (integer) query string variables will be added to the URL.
+On the payment confirmation page, the customer will be prompted to enter their credit card information again and perform any additional actions required by Stripe, such as "3D Secure" confirmation. After confirming their payment, the user will be redirected to the URL provided by the `redirect` parameter specified above. Upon redirection, `message` (string) and `success` (integer) query string variables will be added to the URL.
Alternatively, you could allow Stripe to handle the payment confirmation for you. In this case, instead of redirecting to the payment confirmation page, you may [setup Stripe's automatic billing emails](https://dashboard.stripe.com/account/billing/automatic) in your Stripe dashboard. However, if an `IncompletePayment` exception is caught, you should still inform the user they will receive an email with further payment confirmation instructions.
-Payment exceptions may be thrown for the following methods: `charge`, `invoiceFor`, and `invoice` on the `Billable` user. When handling subscriptions, the `create` method on the `SubscriptionBuilder`, and the `incrementAndInvoice` and `swapAndInvoice` methods on the `Subscription` model may throw exceptions.
+Payment exceptions may be thrown for the following methods: `charge`, `invoiceFor`, and `invoice` on models using the `Billable` trait. When interacting with subscriptions, the `create` method on the `SubscriptionBuilder`, and the `incrementAndInvoice` and `swapAndInvoice` methods on the `Subscription` model may throw incomplete payment exceptions.
+
+Determining if an existing subscription has an incomplete payment may be accomplished using the `hasIncompletePayment` method on the billable model or a subscription instance:
+
+ if ($user->hasIncompletePayment('default')) {
+ //
+ }
+
+ if ($user->subscription('default')->hasIncompletePayment()) {
+ //
+ }
There are currently two types of payment exceptions which extend `IncompletePayment`. You can catch these separately if needed so that you can customize the user experience:
-- `PaymentActionRequired`: this indicates that Stripe requires extra verification in order to confirm and process a payment.
-- `PaymentFailure`: this indicates that a payment failed for various other reasons, such as being out of available funds.
+- `Laravel\Cashier\Exceptions\PaymentActionRequired`: this exception indicates that Stripe requires extra verification in order to confirm and process a payment.
+- `Laravel\Cashier\Exceptions\PaymentFailure`: this exception indicates that a payment failed for various other reasons, such as being out of available funds.
## Strong Customer Authentication
-If your business is based in Europe you will need to abide by the Strong Customer Authentication (SCA) regulations. These regulations were imposed in September 2019 by the European Union to prevent payment fraud. Luckily, Stripe and Cashier are prepared for building SCA compliant applications.
+If your business is based in Europe you will need to abide by the EU's Strong Customer Authentication (SCA) regulations. These regulations were imposed in September 2019 by the European Union to prevent payment fraud. Luckily, Stripe and Cashier are prepared for building SCA compliant applications.
> {note} Before getting started, review [Stripe's guide on PSD2 and SCA](https://stripe.com/guides/strong-customer-authentication) as well as their [documentation on the new SCA APIs](https://stripe.com/docs/strong-customer-authentication).
### Payments Requiring Additional Confirmation
-SCA regulations often require extra verification in order to confirm and process a payment. When this happens, Cashier will throw a `PaymentActionRequired` exception that informs you that this extra verification is needed. More info on how to handle these exceptions be found [here](#handling-failed-payments).
+SCA regulations often require extra verification in order to confirm and process a payment. When this happens, Cashier will throw a `Laravel\Cashier\Exceptions\PaymentActionRequired` exception that informs you that extra verification is needed. More information on how to handle these exceptions be found can be found in the documentation on [handling failed payments](#handling-failed-payments).
#### Incomplete and Past Due State
-When a payment needs additional confirmation, the subscription will remain in an `incomplete` or `past_due` state as indicated by its `stripe_status` database column. Cashier will automatically activate the customer's subscription via a webhook as soon as payment confirmation is complete.
+When a payment needs additional confirmation, the subscription will remain in an `incomplete` or `past_due` state as indicated by its `stripe_status` database column. Cashier will automatically activate the customer's subscription as soon as payment confirmation is complete and your application is notified by Stripe via webhook of its completion.
-For more information on `incomplete` and `past_due` states, please refer to [our additional documentation](#incomplete-and-past-due-status).
+For more information on `incomplete` and `past_due` states, please refer to [our additional documentation on these states](#incomplete-and-past-due-status).
### Off-Session Payment Notifications
-Since SCA regulations require customers to occasionally verify their payment details even while their subscription is active, Cashier can send a payment notification to the customer when off-session payment confirmation is required. For example, this may occur when a subscription is renewing. Cashier's payment notification can be enabled by setting the `CASHIER_PAYMENT_NOTIFICATION` environment variable to a notification class. By default, this notification is disabled. Of course, Cashier includes a notification class you may use for this purpose, but you are free to provide your own notification class if desired:
+Since SCA regulations require customers to occasionally verify their payment details even while their subscription is active, Cashier can send a notification to the customer when off-session payment confirmation is required. For example, this may occur when a subscription is renewing. Cashier's payment notification can be enabled by setting the `CASHIER_PAYMENT_NOTIFICATION` environment variable to a notification class. By default, this notification is disabled. Of course, Cashier includes a notification class you may use for this purpose, but you are free to provide your own notification class if desired:
CASHIER_PAYMENT_NOTIFICATION=Laravel\Cashier\Notifications\ConfirmPayment
@@ -1204,7 +1294,7 @@ Many of Cashier's objects are wrappers around Stripe SDK objects. If you would l
$stripeSubscription->save();
-You may also use the `updateStripeSubscription` method to update the Stripe subscription directly:
+You may also use the `updateStripeSubscription` method to update a Stripe subscription directly:
$subscription->updateStripeSubscription(['application_fee_percent' => 5]);
@@ -1219,6 +1309,6 @@ To get started, add the **testing** version of your Stripe secret to your `phpun
-Now, whenever you interact with Cashier while testing, it will send actual API requests to your Stripe testing environment. For convenience, you should pre-fill your Stripe testing account with subscriptions / plans that you may then use during testing.
+Now, whenever you interact with Cashier while testing, it will send actual API requests to your Stripe testing environment. For convenience, you should pre-fill your Stripe testing account with subscriptions / plans that you may use during testing.
> {tip} In order to test a variety of billing scenarios, such as credit card denials and failures, you may use the vast range of [testing card numbers and tokens](https://stripe.com/docs/testing) provided by Stripe.
diff --git a/blade.md b/blade.md
index d8ad878320c..05c8bd23a8d 100644
--- a/blade.md
+++ b/blade.md
@@ -1,33 +1,34 @@
# Blade Templates
- [Introduction](#introduction)
-- [Template Inheritance](#template-inheritance)
- - [Defining A Layout](#defining-a-layout)
- - [Extending A Layout](#extending-a-layout)
- [Displaying Data](#displaying-data)
+ - [HTML Entity Encoding](#html-entity-encoding)
- [Blade & JavaScript Frameworks](#blade-and-javascript-frameworks)
-- [Control Structures](#control-structures)
+- [Blade Directives](#blade-directives)
- [If Statements](#if-statements)
- [Switch Statements](#switch-statements)
- [Loops](#loops)
- [The Loop Variable](#the-loop-variable)
- [Comments](#comments)
- - [PHP](#php)
+ - [Including Subviews](#including-subviews)
- [The `@once` Directive](#the-once-directive)
-- [Forms](#forms)
- - [CSRF Field](#csrf-field)
- - [Method Field](#method-field)
- - [Validation Errors](#validation-errors)
+ - [Raw PHP](#raw-php)
- [Components](#components)
- - [Displaying Components](#displaying-components)
+ - [Rendering Components](#rendering-components)
- [Passing Data To Components](#passing-data-to-components)
- - [Managing Attributes](#managing-attributes)
+ - [Component Attributes](#component-attributes)
- [Slots](#slots)
- [Inline Component Views](#inline-component-views)
- [Anonymous Components](#anonymous-components)
- [Dynamic Components](#dynamic-components)
-- [Including Subviews](#including-subviews)
- - [Rendering Views For Collections](#rendering-views-for-collections)
+ - [Manually Registering Components](#manually-registering-components)
+- [Building Layouts](#building-layouts)
+ - [Layouts Using Components](#layouts-using-components)
+ - [Layouts Using Template Inheritance](#layouts-using-template-inheritance)
+- [Forms](#forms)
+ - [CSRF Field](#csrf-field)
+ - [Method Field](#method-field)
+ - [Validation Errors](#validation-errors)
- [Stacks](#stacks)
- [Service Injection](#service-injection)
- [Extending Blade](#extending-blade)
@@ -36,78 +37,22 @@
## Introduction
-Blade is the simple, yet powerful templating engine provided with Laravel. Unlike other popular PHP templating engines, Blade does not restrict you from using plain PHP code in your views. In fact, all Blade views are compiled into plain PHP code and cached until they are modified, meaning Blade adds essentially zero overhead to your application. Blade view files use the `.blade.php` file extension and are typically stored in the `resources/views` directory.
-
-
-## Template Inheritance
-
-
-### Defining A Layout
-
-Two of the primary benefits of using Blade are _template inheritance_ and _sections_. To get started, let's take a look at a simple example. First, we will examine a "master" page layout. Since most web applications maintain the same general layout across various pages, it's convenient to define this layout as a single Blade view:
-
-
-
-
-
- App Name - @yield('title')
-
-
- @section('sidebar')
- This is the master sidebar.
- @show
-
-
- @yield('content')
-
-
-
-
-As you can see, this file contains typical HTML mark-up. However, take note of the `@section` and `@yield` directives. The `@section` directive, as the name implies, defines a section of content, while the `@yield` directive is used to display the contents of a given section.
-
-Now that we have defined a layout for our application, let's define a child page that inherits the layout.
-
-
-### Extending A Layout
-
-When defining a child view, use the Blade `@extends` directive to specify which layout the child view should "inherit". Views which extend a Blade layout may inject content into the layout's sections using `@section` directives. Remember, as seen in the example above, the contents of these sections will be displayed in the layout using `@yield`:
-
-
-
- @extends('layouts.app')
-
- @section('title', 'Page Title')
+Blade is the simple, yet powerful templating engine that is included with Laravel. Unlike some PHP templating engines, Blade does not restrict you from using plain PHP code in your templates. In fact, all Blade templates are compiled into plain PHP code and cached until they are modified, meaning Blade adds essentially zero overhead to your application. Blade template files use the `.blade.php` file extension and are typically stored in the `resources/views` directory.
- @section('sidebar')
- @@parent
-
-
This is appended to the master sidebar.
- @endsection
-
- @section('content')
-
This is my body content.
- @endsection
-
-In this example, the `sidebar` section is utilizing the `@@parent` directive to append (rather than overwriting) content to the layout's sidebar. The `@@parent` directive will be replaced by the content of the layout when the view is rendered.
-
-> {tip} Contrary to the previous example, this `sidebar` section ends with `@endsection` instead of `@show`. The `@endsection` directive will only define a section while `@show` will define and **immediately yield** the section.
-
-The `@yield` directive also accepts a default value as its second parameter. This value will be rendered if the section being yielded is undefined:
+Blade views may be returned from routes or controller using the global `view` helper. Of course, as mentioned in the documentation on [views](/docs/{{version}}/views), data may be passed to the Blade view using the `view` helper's second argument:
- @yield('content', View::make('view.name'))
-
-Blade views may be returned from routes using the global `view` helper:
-
- Route::get('blade', function () {
- return view('child');
+ Route::get('/', function () {
+ return view('greeting', ['name' => 'Finn']);
});
+> {tip} Before digging deeper into Blade, make sure to read the Laravel [view documentation](/docs/{{version}}/views).
+
## Displaying Data
-You may display data passed to your Blade views by wrapping the variable in curly braces. For example, given the following route:
+You may display data that is passed to your Blade views by wrapping the variable in curly braces. For example, given the following route:
- Route::get('greeting', function () {
+ Route::get('/', function () {
return view('welcome', ['name' => 'Samantha']);
});
@@ -115,21 +60,12 @@ You may display the contents of the `name` variable like so:
Hello, {{ $name }}.
-> {tip} Blade `{{ }}` statements are automatically sent through PHP's `htmlspecialchars` function to prevent XSS attacks.
+> {tip} Blade's `{{ }}` echo statements are automatically sent through PHP's `htmlspecialchars` function to prevent XSS attacks.
You are not limited to displaying the contents of the variables passed to the view. You may also echo the results of any PHP function. In fact, you can put any PHP code you wish inside of a Blade echo statement:
The current UNIX timestamp is {{ time() }}.
-
-#### Displaying Unescaped Data
-
-By default, Blade `{{ }}` statements are automatically sent through PHP's `htmlspecialchars` function to prevent XSS attacks. If you do not want your data to be escaped, you may use the following syntax:
-
- Hello, {!! $name !!}.
-
-> {note} Be very careful when echoing content that is supplied by users of your application. Always use the escaped, double curly brace syntax to prevent XSS attacks when displaying user supplied data.
-
#### Rendering JSON
@@ -150,7 +86,7 @@ However, instead of manually calling `json_encode`, you may use the `@json` Blad
> {note} You should only use the `@json` directive to render existing variables as JSON. The Blade templating is based on regular expressions and attempts to pass a complex expression to the directive may cause unexpected failures.
-#### HTML Entity Encoding
+### HTML Entity Encoding
By default, Blade (and the Laravel `e` helper) will double encode HTML entities. If you would like to disable double encoding, call the `Blade::withoutDoubleEncoding` method from the `boot` method of your `AppServiceProvider`:
@@ -174,6 +110,15 @@ By default, Blade (and the Laravel `e` helper) will double encode HTML entities.
}
}
+
+#### Displaying Unescaped Data
+
+By default, Blade `{{ }}` statements are automatically sent through PHP's `htmlspecialchars` function to prevent XSS attacks. If you do not want your data to be escaped, you may use the following syntax:
+
+ Hello, {!! $name !!}.
+
+> {note} Be very careful when echoing content that is supplied by users of your application. You should typically use the escaped, double curly brace syntax to prevent XSS attacks when displaying user supplied data.
+
### Blade & JavaScript Frameworks
@@ -183,11 +128,11 @@ Since many JavaScript frameworks also use "curly" braces to indicate a given exp
Hello, @{{ name }}.
-In this example, the `@` symbol will be removed by Blade; however, `{{ name }}` expression will remain untouched by the Blade engine, allowing it to instead be rendered by your JavaScript framework.
+In this example, the `@` symbol will be removed by Blade; however, `{{ name }}` expression will remain untouched by the Blade engine, allowing it to be rendered by your JavaScript framework.
The `@` symbol may also be used to escape Blade directives:
- {{-- Blade --}}
+ {{-- Blade template --}}
@@json()
@@ -204,10 +149,10 @@ If you are displaying JavaScript variables in a large portion of your template,
@endverbatim
-
-## Control Structures
+
+## Blade Directives
-In addition to template inheritance and displaying data, Blade also provides convenient shortcuts for common PHP control structures, such as conditional statements and loops. These shortcuts provide a very clean, terse way of working with PHP control structures, while also remaining familiar to their PHP counterparts.
+In addition to template inheritance and displaying data, Blade also provides convenient shortcuts for common PHP control structures, such as conditional statements and loops. These shortcuts provide a very clean, terse way of working with PHP control structures while also remaining familiar to their PHP counterparts.
### If Statements
@@ -241,7 +186,7 @@ In addition to the conditional directives already discussed, the `@isset` and `@
#### Authentication Directives
-The `@auth` and `@guest` directives may be used to quickly determine if the current user is authenticated or is a guest:
+The `@auth` and `@guest` directives may be used to quickly determine if the current user is [authenticated](/docs/{{version}}/authentication) or is a guest:
@auth
// The user is authenticated...
@@ -251,7 +196,7 @@ The `@auth` and `@guest` directives may be used to quickly determine if the curr
// The user is not authenticated...
@endguest
-If needed, you may specify the [authentication guard](/docs/{{version}}/authentication) that should be checked when using the `@auth` and `@guest` directives:
+If needed, you may specify the authentication guard that should be checked when using the `@auth` and `@guest` directives:
@auth('admin')
// The user is authenticated...
@@ -261,27 +206,6 @@ If needed, you may specify the [authentication guard](/docs/{{version}}/authenti
// The user is not authenticated...
@endguest
-
-#### Section Directives
-
-You may check if a section has content using the `@hasSection` directive:
-
- @hasSection('navigation')
-
- @yield('navigation')
-
-
-
- @endif
-
-You may use the `sectionMissing` directive to determine if a section does not have content:
-
- @sectionMissing('navigation')
-
- @include('default-navigation')
-
- @endif
-
#### Environment Directives
@@ -301,6 +225,31 @@ Or, you may determine if the application is running in a specific environment us
// The application is running in "staging" or "production"...
@endenv
+
+#### Section Directives
+
+You may determine if a template inheritance section has content using the `@hasSection` directive:
+
+```html
+@hasSection('navigation')
+
+ @yield('navigation')
+
+
+
+@endif
+```
+
+You may use the `sectionMissing` directive to determine if a section does not have content:
+
+```html
+@sectionMissing('navigation')
+
+ @include('default-navigation')
+
+@endif
+```
+
### Switch Statements
@@ -344,7 +293,7 @@ In addition to conditional statements, Blade provides simple directives for work
> {tip} When looping, you may use the [loop variable](#the-loop-variable) to gain valuable information about the loop, such as whether you are in the first or last iteration through the loop.
-When using loops you may also end the loop or skip the current iteration:
+When using loops you may also end the loop or skip the current iteration using the `@continue` and `@break` directives:
@foreach ($users as $user)
@if ($user->type == 1)
@@ -358,7 +307,7 @@ When using loops you may also end the loop or skip the current iteration:
@endif
@endforeach
-You may also include the condition with the directive declaration in one line:
+You may also include the continuation or break condition within the directive declaration:
@foreach ($users as $user)
@continue($user->type == 1)
@@ -417,16 +366,57 @@ Blade also allows you to define comments in your views. However, unlike HTML com
{{-- This comment will not be present in the rendered HTML --}}
-
-### PHP
+
+### Including Subviews
-In some situations, it's useful to embed PHP code into your views. You can use the Blade `@php` directive to execute a block of plain PHP within your template:
+> {tip} While you're free to use the `@include` directive, Blade [components](#components) provide similar functionality and offer several benefits over the `@include` directive such as data and attribute binding.
- @php
- //
- @endphp
+Blade's `@include` directive allows you to include a Blade view from within another view. All variables that are available to the parent view will be made available to the included view:
+
+```html
+
+ @include('shared.errors')
+
+
+
+```
+
+Even though the included view will inherit all data available in the parent view, you may also pass an array of additional data that should be made available to the included view:
+
+ @include('view.name', ['status' => 'complete'])
+
+If you attempt to `@include` a view which does not exist, Laravel will throw an error. If you would like to include a view that may or may not be present, you should use the `@includeIf` directive:
+
+ @includeIf('view.name', ['status' => 'complete'])
+
+If you would like to `@include` a view if a given boolean expression evaluates to `true` or `false`, you may use the `@includeWhen` and `@includeUnless` directives:
+
+ @includeWhen($boolean, 'view.name', ['status' => 'complete'])
+
+ @includeUnless($boolean, 'view.name', ['status' => 'complete'])
+
+To include the first view that exists from a given array of views, you may use the `includeFirst` directive:
+
+ @includeFirst(['custom.admin', 'admin'], ['status' => 'complete'])
+
+> {note} You should avoid using the `__DIR__` and `__FILE__` constants in your Blade views, since they will refer to the location of the cached, compiled view.
+
+
+#### Rendering Views For Collections
-> {tip} While Blade provides this feature, using it frequently may be a signal that you have too much logic embedded within your template.
+You may combine loops and includes into one line with Blade's `@each` directive:
+
+ @each('view.name', $jobs, 'job')
+
+The `@each` directive's first argument is the view to render for each element in the array or collection. The second argument is the array or collection you wish to iterate over, while the third argument is the variable name that will be assigned to the current iteration within the view. So, for example, if you are iterating over an array of `jobs`, typically you will want to access each job as a `job` variable within the view. The array key for the current iteration will be available as the `key` variable within the view.
+
+You may also pass a fourth argument to the `@each` directive. This argument determines the view that will be rendered if the given array is empty.
+
+ @each('view.name', $jobs, 'job', 'view.empty')
+
+> {note} Views rendered via `@each` do not inherit the variables from the parent view. If the child view requires these variables, you should use the `@foreach` and `@include` directives instead.
### The `@once` Directive
@@ -441,6 +431,137 @@ The `@once` directive allows you to define a portion of the template that will o
@endpush
@endonce
+
+## Building Layouts
+
+
+### Layouts Using Components
+
+Most web applications maintain the same general layout across various pages. It would be incredibly cumbersome and hard to maintain our application if we had to repeat the entire layout HTML in every view we create. Thankfully, it's convenient to define this layout as a single [Blade component](#components) and then use it throughout our application.
+
+
+#### Defining The Layout Component
+
+For example, imagine we are building a "todo" list application. We might define a `layout` component that looks like the following:
+
+```html
+
+
+
+
+ {{ $title ?? 'Todo Manager' }}
+
+
+
Todos
+
+ {{ $slot }}
+
+
+```
+
+
+#### Applying The Layout Component
+
+Once the `layout` component has been defined, we may create a Blade view that utilizes the component. In this example, we will define a simple view that displays our task list:
+
+```html
+
+
+
+ @foreach ($tasks as $task)
+ {{ $task }}
+ @endforeach
+
+```
+
+Remember, content that is injected into a component will be supplied to the default `$slot` variable within our `layout` component. As you may have noticed, our `layout` also respects a `$title` slot if one is provided; otherwise, a default title is shown. We may inject a custom title from our task list view using the standard slot syntax discussed in the [component documentation](#components):
+
+```html
+
+
+
+
+ Custom Title
+
+
+ @foreach ($tasks as $task)
+ {{ $task }}
+ @endforeach
+
+```
+
+Now that we have defined our layout and task list views, we just need to return the `task` view from a route:
+
+ use App\Models\Task;
+
+ Route::get('/tasks', function () {
+ return view('tasks', ['tasks' => Task::all()]);
+ });
+
+
+### Layouts Using Template Inheritance
+
+
+#### Defining A Layout
+
+Layouts may also be created via "template inheritance". This was the primary way of building applications prior to the introduction of [components](#components).
+
+To get started, let's take a look at a simple example. First, we will examine a page layout. Since most web applications maintain the same general layout across various pages, it's convenient to define this layout as a single Blade view:
+
+```html
+
+
+
+
+ App Name - @yield('title')
+
+
+ @section('sidebar')
+ This is the master sidebar.
+ @show
+
+
+ @yield('content')
+
+
+
+```
+
+As you can see, this file contains typical HTML mark-up. However, take note of the `@section` and `@yield` directives. The `@section` directive, as the name implies, defines a section of content, while the `@yield` directive is used to display the contents of a given section.
+
+Now that we have defined a layout for our application, let's define a child page that inherits the layout.
+
+
+#### Extending A Layout
+
+When defining a child view, use the `@extends` Blade directive to specify which layout the child view should "inherit". Views which extend a Blade layout may inject content into the layout's sections using `@section` directives. Remember, as seen in the example above, the contents of these sections will be displayed in the layout using `@yield`:
+
+```html
+
+
+@extends('layouts.app')
+
+@section('title', 'Page Title')
+
+@section('sidebar')
+ @@parent
+
+
This is appended to the master sidebar.
+@endsection
+
+@section('content')
+
This is my body content.
+@endsection
+```
+
+In this example, the `sidebar` section is utilizing the `@@parent` directive to append (rather than overwriting) content to the layout's sidebar. The `@@parent` directive will be replaced by the content of the layout when the view is rendered.
+
+> {tip} Contrary to the previous example, this `sidebar` section ends with `@endsection` instead of `@show`. The `@endsection` directive will only define a section while `@show` will define and **immediately yield** the section.
+
+The `@yield` directive also accepts a default value as its second parameter. This value will be rendered if the section being yielded is undefined:
+
+ @yield('content', 'Default content')
+
## Forms
@@ -449,60 +570,77 @@ The `@once` directive allows you to define a portion of the template that will o
Anytime you define an HTML form in your application, you should include a hidden CSRF token field in the form so that [the CSRF protection](https://laravel.com/docs/{{version}}/csrf) middleware can validate the request. You may use the `@csrf` Blade directive to generate the token field:
-
+ ...
+
+```
### Method Field
Since HTML forms can't make `PUT`, `PATCH`, or `DELETE` requests, you will need to add a hidden `_method` field to spoof these HTTP verbs. The `@method` Blade directive can create this field for you:
-
+ ...
+
+```
### Validation Errors
The `@error` directive may be used to quickly check if [validation error messages](/docs/{{version}}/validation#quick-displaying-the-validation-errors) exist for a given attribute. Within an `@error` directive, you may echo the `$message` variable to display the error message:
-
+```html
+
-
+
-
+
- @error('title')
-
{{ $message }}
- @enderror
+@error('title')
+
{{ $message }}
+@enderror
+```
You may pass [the name of a specific error bag](/docs/{{version}}/validation#named-error-bags) as the second parameter to the `@error` directive to retrieve validation error messages on pages containing multiple forms:
-
+```html
+
+
+
-
+
-
+@error('email', 'login')
+
{{ $message }}
+@enderror
+```
- @error('email', 'login')
-
{{ $message }}
- @enderror
+
+### Raw PHP
+
+In some situations, it's useful to embed PHP code into your views. You can use the Blade `@php` directive to execute a block of plain PHP within your template:
+
+ @php
+ $counter = 1;
+ @endphp
## Components
-Components and slots provide similar benefits to sections and layouts; however, some may find the mental model of components and slots easier to understand. There are two approaches to writing components: class based components and anonymous components.
+Components and slots provide similar benefits to sections, layouts, and includes; however, some may find the mental model of components and slots easier to understand. There are two approaches to writing components: class based components and anonymous components.
To create a class based component, you may use the `make:component` Artisan command. To illustrate how to use components, we will create a simple `Alert` component. The `make:component` command will place the component in the `App\View\Components` directory:
php artisan make:component Alert
-The `make:component` command will also create a view template for the component. The view will be placed in the `resources/views/components` directory.
+The `make:component` command will also create a view template for the component. The view will be placed in the `resources/views/components` directory. When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory, so no further component registration is typically required.
You may also create components within subdirectories:
@@ -528,30 +666,10 @@ However, if you are building a package that utilizes Blade components, you will
}
Once your component has been registered, it may be rendered using its tag alias:
+>>>>>>> 8.x
-
-
-Alternatively, you may use the `componentNamespace` method to autoload component classes by convention. For example, a `Nightshade` package might have `Calendar` and `ColorPicker` components that reside within the `Package\Views\Components` namespace:
-
- use Illuminate\Support\Facades\Blade;
-
- /**
- * Bootstrap your package's services.
- */
- public function boot()
- {
- Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
- }
-
-This will allow the usage of package components by their vendor namespace using the `package-name::` syntax:
-
-
-
-
-Blade will automatically detect the class that's linked to this component by pascal-casing the component name. Subdirectories are also supported using "dot" notation.
-
-
-### Displaying Components
+
+### Rendering Components
To display a component, you may use a Blade component tag within one of your Blade templates. Blade component tags start with the string `x-` followed by the kebab case name of the component class:
@@ -566,7 +684,7 @@ If the component class is nested deeper within the `App\View\Components` directo
### Passing Data To Components
-You may pass data to Blade components using HTML attributes. Hard-coded, primitive values may be passed to the component using simple HTML attributes. PHP expressions and variables should be passed to the component via attributes that use the `:` character as a prefix:
+You may pass data to Blade components using HTML attributes. Hard-coded, primitive values may be passed to the component using simple HTML attribute strings. PHP expressions and variables should be passed to the component via attributes that use the `:` character as a prefix:
@@ -620,9 +738,11 @@ You should define the component's required data in its class constructor. All pu
When your component is rendered, you may display the contents of your component's public variables by echoing the variables by name:
-
- {{ $message }}
-
+```html
+
+ {{ $message }}
+
+```
#### Casing
@@ -640,14 +760,14 @@ Component constructor arguments should be specified using `camelCase`, while `ke
$this->alertType = $alertType;
}
-The `$alertType` argument may be provided like so:
+The `$alertType` argument may be provided to the component like so:
#### Component Methods
-In addition to public variables being available to your component template, any public methods on the component may also be executed. For example, imagine a component that has a `isSelected` method:
+In addition to public variables being available to your component template, any public methods on the component may be invoked. For example, imagine a component that has a `isSelected` method:
/**
* Determine if the given option is the current selected option.
@@ -666,10 +786,10 @@ You may execute this method from your component template by invoking the variabl
{{ $label }}
-
-#### Using Attributes & Slots Inside The Class
+
+#### Accessing Attributes & Slots Within Component Classes
-Blade components also allow you to access the component name, attributes, and slot inside the class's render method. However, in order to access this data, you should return a Closure from your component's `render` method. The Closure will receive a `$data` array as its only argument:
+Blade components also allow you to access the component name, attributes, and slot inside the class's render method. However, in order to access this data, you should return a closure from your component's `render` method. The closure will receive a `$data` array as its only argument. This array will contain several elements that provide information about the component:
/**
* Get the view / contents that represent the component.
@@ -687,9 +807,9 @@ Blade components also allow you to access the component name, attributes, and sl
};
}
-The `componentName` is equal to the name used in the HTML tag after the `x-` prefix. So ``'s `componentName` will be `alert`. The `attributes` element will contain all of the attributes that were present on the HTML tag. The `slot` element is an `Illuminate\Support\HtmlString` instance with the contents of the slot from the component.
+The `componentName` is equal to the name used in the HTML tag after the `x-` prefix. So ``'s `componentName` will be `alert`. The `attributes` element will contain all of the attributes that were present on the HTML tag. The `slot` element is an `Illuminate\Support\HtmlString` instance with the contents of the component's slot.
-The Closure should return a string. If the returned string corresponds to an existing view, that view will be rendered; otherwise, the returned string will be evaluated as an inline Blade view.
+The closure should return a string. If the returned string corresponds to an existing view, that view will be rendered; otherwise, the returned string will be evaluated as an inline Blade view.
#### Additional Dependencies
@@ -713,8 +833,8 @@ If your component requires dependencies from Laravel's [service container](/docs
$this->message = $message;
}
-
-### Managing Attributes
+
+### Component Attributes
We've already examined how to pass data attributes to a component; however, sometimes you may need to specify additional HTML attributes, such as `class`, that are not part of the data required for a component to function. Typically, you want to pass these additional attributes down to the root element of the component template. For example, imagine we want to render an `alert` component like so:
@@ -723,15 +843,15 @@ We've already examined how to pass data attributes to a component; however, some
All of the attributes that are not part of the component's constructor will automatically be added to the component's "attribute bag". This attribute bag is automatically made available to the component via the `$attributes` variable. All of the attributes may be rendered within the component by echoing this variable:
-
+
-> {note} Using directives such as `@env` directly on a component is not supported at this time.
+> {note} Using directives such as `@env` within component tags is not supported at this time. For example, `` will not be compiled.
#### Default / Merged Attributes
-Sometimes you may need to specify default values for attributes or merge additional values into some of the component's attributes. To accomplish this, you may use the attribute bag's `merge` method:
+Sometimes you may need to specify default values for attributes or merge additional values into some of the component's attributes. To accomplish this, you may use the attribute bag's `merge` method. This method is particularly useful for defining a set a default CSS classes that should always be applied to a component:
merge(['class' => 'alert alert-'.$type]) }}>
{{ $message }}
@@ -743,14 +863,16 @@ If we assume this component is utilized like so:
The final, rendered HTML of the component will appear like the following:
-
-
-
+```html
+
+
+
+```
#### Non-Class Attribute Merging
-When merging attributes that are not `class` attributes, the values provided to the `merge` method will be considered the "default" values of attribute which can be overwritten by the component's consumer. Unlike `class` attributes, non-class attributes are not appended to each other. For example, a `button` component may look like the following:
+When merging attributes that are not `class` attributes, the values provided to the `merge` method will be considered the "default" values of attribute. However, unlike the `class` attribute, these attributes will not be merged with injected attribute values. Instead, they will be overwritten. For example, a `button` component's implementation may look like the following:
-If you would like an attribute other than `class` to have its values appended together, you may use the `prepends` method:
+If you would like an attribute other than `class` to have its default value and injected values joined together, you may use the `prepends` method. In this example, the `data-controller` attribute will always begin with `profile-controller` and any additional injected `data-controller` values will be placed after this default value:
merge(['data-controller' => $attributes->prepends('profile-controller')]) }}>
{{ $slot }}
@@ -777,7 +899,7 @@ If you would like an attribute other than `class` to have its values appended to
#### Filtering Attributes
-You may filter attributes using the `filter` method. This method accepts a Closure which should return `true` if you wish to retain the attribute in the attribute bag:
+You may filter attributes using the `filter` method. This method accepts a closure which should return `true` if you wish to retain the attribute in the attribute bag:
{{ $attributes->filter(fn ($value, $key) => $key == 'foo') }}
@@ -792,52 +914,62 @@ Using the `first` method, you may render the first attribute in a given attribut
### Slots
-Often, you will need to pass additional content to your component via "slots". Let's imagine that an `alert` component we created has the following markup:
+You will often need to pass additional content to your component via "slots". Component slots are rendered by echoing the `$slot` variable. To explore this concept, let's imagine that an `alert` component has the following markup:
-
+```html
+
-
- {{ $slot }}
-
+
+ {{ $slot }}
+
+```
We may pass content to the `slot` by injecting content into the component:
-
- Whoops! Something went wrong!
-
+```html
+
+ Whoops! Something went wrong!
+
+```
-Sometimes a component may need to render multiple different slots in different locations within the component. Let's modify our alert component to allow for the injection of a "title":
+Sometimes a component may need to render multiple different slots in different locations within the component. Let's modify our alert component to allow for the injection of a "title" slot:
-
+```html
+
- {{ $title }}
+{{ $title }}
-
- {{ $slot }}
-
+
+ {{ $slot }}
+
+```
-You may define the content of the named slot using the `x-slot` tag. Any content not within an `x-slot` tag will be passed to the component in the `$slot` variable:
+You may define the content of the named slot using the `x-slot` tag. Any content not within an explicit `x-slot` tag will be passed to the component in the `$slot` variable:
-
-
- Server Error
-
+```html
+
+
+ Server Error
+
- Whoops! Something went wrong!
-
+ Whoops! Something went wrong!
+
+```
#### Scoped Slots
-If you have used a JavaScript framework such as Vue, you may be familiar with "scoped slots", which allow you to access data or methods from the component within your slot. You may achieve similar behavior in Laravel by defining public methods or properties on your component and accessing the component within your slot via the `$component` variable:
+If you have used a JavaScript framework such as Vue, you may be familiar with "scoped slots", which allow you to access data or methods from the component within your slot. You may achieve similar behavior in Laravel by defining public methods or properties on your component and accessing the component within your slot via the `$component` variable. In this example, we will assume that the `x-alert` component has a public `formatAlert` method defined on its component class:
-
-
- {{ $component->formatAlert('Server Error') }}
-
+```html
+
+
+ {{ $component->formatAlert('Server Error') }}
+
- Whoops! Something went wrong!
-
+ Whoops! Something went wrong!
+
+```
### Inline Component Views
@@ -868,7 +1000,7 @@ To create a component that renders an inline view, you may use the `inline` opti
### Anonymous Components
-Similar to inline components, anonymous components provide a mechanism for managing a component via a single file. However, anonymous components utilize a single view file and have no associated class. To define an anonymous component, you only need to place a Blade template within your `resources/views/components` directory. For example, assuming you have defined a component at `resources/views/components/alert.blade.php`:
+Similar to inline components, anonymous components provide a mechanism for managing a component via a single file. However, anonymous components utilize a single view file and have no associated class. To define an anonymous component, you only need to place a Blade template within your `resources/views/components` directory. For example, assuming you have defined a component at `resources/views/components/alert.blade.php`, you may simply render it like so:
@@ -891,6 +1023,10 @@ You may specify which attributes should be considered data variables using the `
{{ $message }}
+Given the component definition above, we may render the component like so:
+
+
+
### Dynamic Components
@@ -898,112 +1034,102 @@ Sometimes you may need to render a component but not know which component should
-
-## Including Subviews
-
-Blade's `@include` directive allows you to include a Blade view from within another view. All variables that are available to the parent view will be made available to the included view:
-
-
- @include('shared.errors')
-
-
-
-
-Even though the included view will inherit all data available in the parent view, you may also pass an array of extra data to the included view:
-
- @include('view.name', ['some' => 'data'])
-
-If you attempt to `@include` a view which does not exist, Laravel will throw an error. If you would like to include a view that may or may not be present, you should use the `@includeIf` directive:
+
+### Manually Registering Components
- @includeIf('view.name', ['some' => 'data'])
+> {note} The following documentation on manually registering components is primarily applicable to those who are writing Laravel packages that include view components. If you are not writing a package, this portion of the component documentation may not be relevant to you.
-If you would like to `@include` a view if a given boolean expression evaluates to `true`, you may use the `@includeWhen` directive:
-
- @includeWhen($boolean, 'view.name', ['some' => 'data'])
-
-If you would like to `@include` a view if a given boolean expression evaluates to `false`, you may use the `@includeUnless` directive:
-
- @includeUnless($boolean, 'view.name', ['some' => 'data'])
-
-To include the first view that exists from a given array of views, you may use the `includeFirst` directive:
-
- @includeFirst(['custom.admin', 'admin'], ['some' => 'data'])
-
-> {note} You should avoid using the `__DIR__` and `__FILE__` constants in your Blade views, since they will refer to the location of the cached, compiled view.
-
-
-#### Aliasing Includes
-
-If your Blade includes are stored in a subdirectory, you may wish to alias them for easier access. For example, imagine a Blade include that is stored at `resources/views/includes/input.blade.php` with the following content:
-
-
+When writing components for your own application, components are automatically discovered within the `app/View/Components` directory and `resources/views/components` directory.
-You may use the `include` method to alias the include from `includes.input` to `input`. Typically, this should be done in the `boot` method of your `AppServiceProvider`:
+However, if you are building a package that utilizes Blade components or placing components in non-conventional directories, you will need to manually register your component class and its HTML tag alias so that Laravel knows where to find the component. You should typically register your components in the `boot` method of your package's service provider:
use Illuminate\Support\Facades\Blade;
+ use VendorPackage\View\Components\AlertComponent;
- Blade::include('includes.input', 'input');
+ /**
+ * Bootstrap your package's services.
+ *
+ * @return void
+ */
+ public function boot()
+ {
+ Blade::component('package-alert', AlertComponent::class);
+ }
-Once the include has been aliased, you may render it using the alias name as the Blade directive:
+Once your component has been registered, it may be rendered using its tag alias:
- @input(['type' => 'email'])
+
-
-### Rendering Views For Collections
+#### Autoloading Package Components
-You may combine loops and includes into one line with Blade's `@each` directive:
+Alternatively, you may use the `componentNamespace` method to autoload component classes by convention. For example, a `Nightshade` package might have `Calendar` and `ColorPicker` components that reside within the `Package\Views\Components` namespace:
- @each('view.name', $jobs, 'job')
+ use Illuminate\Support\Facades\Blade;
-The first argument is the view partial to render for each element in the array or collection. The second argument is the array or collection you wish to iterate over, while the third argument is the variable name that will be assigned to the current iteration within the view. So, for example, if you are iterating over an array of `jobs`, typically you will want to access each job as a `job` variable within your view partial. The key for the current iteration will be available as the `key` variable within your view partial.
+ /**
+ * Bootstrap your package's services.
+ *
+ * @return void
+ */
+ public function boot()
+ {
+ Blade::componentNamespace('Nightshade\\Views\\Components', 'nightshade');
+ }
-You may also pass a fourth argument to the `@each` directive. This argument determines the view that will be rendered if the given array is empty.
+This will allow the usage of package components by their vendor namespace using the `package-name::` syntax:
- @each('view.name', $jobs, 'job', 'view.empty')
+
+
-> {note} Views rendered via `@each` do not inherit the variables from the parent view. If the child view requires these variables, you should use `@foreach` and `@include` instead.
+Blade will automatically detect the class that's linked to this component by pascal-casing the component name. Subdirectories are also supported using "dot" notation.
## Stacks
Blade allows you to push to named stacks which can be rendered somewhere else in another view or layout. This can be particularly useful for specifying any JavaScript libraries required by your child views:
- @push('scripts')
-
- @endpush
+```html
+@push('scripts')
+
+@endpush
+```
You may push to a stack as many times as needed. To render the complete stack contents, pass the name of the stack to the `@stack` directive:
-
-
+```html
+
+
- @stack('scripts')
-
+ @stack('scripts')
+
+```
If you would like to prepend content onto the beginning of a stack, you should use the `@prepend` directive:
- @push('scripts')
- This will be second...
- @endpush
+```html
+@push('scripts')
+ This will be second...
+@endpush
- // Later...
+// Later...
- @prepend('scripts')
- This will be first...
- @endprepend
+@prepend('scripts')
+ This will be first...
+@endprepend
+```
## Service Injection
The `@inject` directive may be used to retrieve a service from the Laravel [service container](/docs/{{version}}/container). The first argument passed to `@inject` is the name of the variable the service will be placed into, while the second argument is the class or interface name of the service you wish to resolve:
- @inject('metrics', 'App\Services\MetricsService')
+```html
+@inject('metrics', 'App\Services\MetricsService')
-
+```
## Extending Blade
@@ -1053,7 +1179,7 @@ As you can see, we will chain the `format` method onto whatever expression is pa
### Custom If Statements
-Programming a custom directive is sometimes more complex than necessary when defining simple, custom conditional statements. For that reason, Blade provides a `Blade::if` method which allows you to quickly define custom conditional directives using Closures. For example, let's define a custom conditional that checks the current application cloud provider. We may do this in the `boot` method of our `AppServiceProvider`:
+Programming a custom directive is sometimes more complex than necessary when defining simple, custom conditional statements. For that reason, Blade provides a `Blade::if` method which allows you to quickly define custom conditional directives using closures. For example, let's define a custom conditional that checks the configured default "disk" for the application. We may do this in the `boot` method of our `AppServiceProvider`:
use Illuminate\Support\Facades\Blade;
@@ -1064,21 +1190,23 @@ Programming a custom directive is sometimes more complex than necessary when def
*/
public function boot()
{
- Blade::if('cloud', function ($provider) {
- return config('filesystems.default') === $provider;
+ Blade::if('disk', function ($value) {
+ return config('filesystems.default') === $value;
});
}
-Once the custom conditional has been defined, we can easily use it on our templates:
-
- @cloud('digitalocean')
- // The application is using the digitalocean cloud provider...
- @elsecloud('aws')
- // The application is using the aws provider...
- @else
- // The application is not using the digitalocean or aws environment...
- @endcloud
-
- @unlesscloud('aws')
- // The application is not using the aws environment...
- @endcloud
+Once the custom conditional has been defined, you can use it within your templates:
+
+```html
+@disk('local')
+
+@elsedisk('s3')
+
+@else
+
+@enddisk
+
+@unlessdisk('local')
+
+@enddisk
+```
diff --git a/broadcasting.md b/broadcasting.md
index 7b7be6557ce..1e9acacb67c 100644
--- a/broadcasting.md
+++ b/broadcasting.md
@@ -1,8 +1,14 @@
# Broadcasting
- [Introduction](#introduction)
+- [Server Side Installation](#server-side-installation)
- [Configuration](#configuration)
- - [Driver Prerequisites](#driver-prerequisites)
+ - [Pusher Channels](#pusher-channels)
+ - [Ably](#ably)
+ - [Open Source Alternatives](#open-source-alternatives)
+- [Client Side Installation](#client-side-installation)
+ - [Pusher Channels](#client-pusher-channels)
+ - [Ably](#client-ably)
- [Concept Overview](#concept-overview)
- [Using An Example Application](#using-example-application)
- [Defining Broadcast Events](#defining-broadcast-events)
@@ -17,7 +23,6 @@
- [Broadcasting Events](#broadcasting-events)
- [Only To Others](#only-to-others)
- [Receiving Broadcasts](#receiving-broadcasts)
- - [Installing Laravel Echo](#installing-laravel-echo)
- [Listening For Events](#listening-for-events)
- [Leaving A Channel](#leaving-a-channel)
- [Namespaces](#namespaces)
@@ -31,110 +36,182 @@
## Introduction
-In many modern web applications, WebSockets are used to implement realtime, live-updating user interfaces. When some data is updated on the server, a message is typically sent over a WebSocket connection to be handled by the client. This provides a more robust, efficient alternative to continually polling your application for changes.
+In many modern web applications, WebSockets are used to implement realtime, live-updating user interfaces. When some data is updated on the server, a message is typically sent over a WebSocket connection to be handled by the client. WebSockets provide a more efficient alternative to continually polling your application's server for data changes that should be reflected in your UI.
-To assist you in building these types of applications, Laravel makes it easy to "broadcast" your [events](/docs/{{version}}/events) over a WebSocket connection. Broadcasting your Laravel events allows you to share the same event names between your server-side code and your client-side JavaScript application.
+For example, imagine your application is able to export a user's data to a CSV file and email it to them. However, creating this CSV file takes several minutes so you choose to create and mail the CSV within a [queued job](/docs/{{version}}/queues). When the CSV has been created and mailed to the user, we can use event broadcasting to dispatch a `App\Events\UserDataExported` event that is received by our application's JavaScript. Once the event is received, we can display a message to the user that their CSV has been emailed to them without them ever needing to refresh the page.
-> {tip} Before diving into event broadcasting, make sure you have read all of the documentation regarding Laravel [events and listeners](/docs/{{version}}/events).
+To assist you in building these types of features, Laravel makes it easy to "broadcast" your server-side Laravel [events](/docs/{{version}}/events) over a WebSocket connection. Broadcasting your Laravel events allows you to share the same event names and data between your server-side Laravel application and your client-side JavaScript application.
+
+
+#### Supported Drivers
+
+By default, Laravel includes two server-side broadcasting drivers for you to choose from: [Pusher Channels](https://pusher.com/channels) and [Ably](https://ably.io). However, community driven packages such as [laravel-websockets](https://beyondco.de/docs/laravel-websockets/getting-started/introduction) provide additional broadcasting drivers that do not require commercial broadcasting providers.
+
+> {tip} Before diving into event broadcasting, make sure you have read Laravel's documentation on [events and listeners](/docs/{{version}}/events).
+
+
+## Server Side Installation
+
+To get started using Laravel's event broadcasting, we need to do some configuration within the Laravel application as well as install a few packages.
+
+Event broadcasting is accomplished by a server-side broadcasting driver that broadcasts your Laravel events so that Laravel Echo (a JavaScript library) can receive them within the browser client. Don't worry - we'll walk through each part of the installation process step-by-step.
### Configuration
-All of your application's event broadcasting configuration is stored in the `config/broadcasting.php` configuration file. Laravel supports several broadcast drivers out of the box: [Pusher Channels](https://pusher.com/channels), [Redis](/docs/{{version}}/redis), and a `log` driver for local development and debugging. Additionally, a `null` driver is included which allows you to totally disable broadcasting. A configuration example is included for each of these drivers in the `config/broadcasting.php` configuration file.
+All of your application's event broadcasting configuration is stored in the `config/broadcasting.php` configuration file. Laravel supports several broadcast drivers out of the box: [Pusher Channels](https://pusher.com/channels), [Redis](/docs/{{version}}/redis), and a `log` driver for local development and debugging. Additionally, a `null` driver is included which allows you to totally disable broadcasting during testing. A configuration example is included for each of these drivers in the `config/broadcasting.php` configuration file.
#### Broadcast Service Provider
-Before broadcasting any events, you will first need to register the `App\Providers\BroadcastServiceProvider`. In fresh Laravel applications, you only need to uncomment this provider in the `providers` array of your `config/app.php` configuration file. This provider will allow you to register the broadcast authorization routes and callbacks.
+Before broadcasting any events, you will first need to register the `App\Providers\BroadcastServiceProvider`. In a new Laravel applications, you only need to uncomment this provider in the `providers` array of your `config/app.php` configuration file. This `BroadcastServiceProvider` contains the code necessary to register the broadcast authorization routes and callbacks.
-
-#### CSRF Token
+
+#### Queue Configuration
-[Laravel Echo](#installing-laravel-echo) will need access to the current session's CSRF token. You should verify that your application's `head` HTML element defines a `meta` tag containing the CSRF token:
-
-
-
-
-### Driver Prerequisites
+You will also need to configure and run a [queue worker](/docs/{{version}}/queues). All event broadcasting is done via queued jobs so that the response time of your application is not seriously affected by events being broadcast.
-#### Pusher Channels
+### Pusher Channels
-If you are broadcasting your events over [Pusher Channels](https://pusher.com/channels), you should install the Pusher Channels PHP SDK using the Composer package manager:
+If you plan to broadcast your events using [Pusher Channels](https://pusher.com/channels), you should install the Pusher Channels PHP SDK using the Composer package manager:
composer require pusher/pusher-php-server "~4.0"
-Next, you should configure your Channels credentials in the `config/broadcasting.php` configuration file. An example Channels configuration is already included in this file, allowing you to quickly specify your Channels key, secret, and application ID. The `config/broadcasting.php` file's `pusher` configuration also allows you to specify additional `options` that are supported by Channels, such as the cluster:
-
- 'options' => [
- 'cluster' => 'eu',
- 'useTLS' => true
- ],
+Next, you should configure your Pusher Channels credentials in the `config/broadcasting.php` configuration file. An example Pusher Channels configuration is already included in this file, allowing you to quickly specify your key, secret, and application ID. Typically, these values should be set via the `PUSHER_APP_KEY`, `PUSHER_APP_SECRET`, and `PUSHER_APP_ID` [environment variables](/docs/{{version}}/configuration#environment-configuration):
-When using Channels and [Laravel Echo](#installing-laravel-echo), you should specify `pusher` as your desired broadcaster when instantiating the Echo instance in your `resources/js/bootstrap.js` file:
+ PUSHER_APP_ID=your-pusher-app-id
+ PUSHER_APP_KEY=your-pusher-key
+ PUSHER_APP_SECRET=your-pusher-secret
+ PUSHER_APP_CLUSTER=mt1
- import Echo from "laravel-echo";
-
- window.Pusher = require('pusher-js');
-
- window.Echo = new Echo({
- broadcaster: 'pusher',
- key: 'your-pusher-channels-key'
- });
+The `config/broadcasting.php` file's `pusher` configuration also allows you to specify additional `options` that are supported by Channels, such as the cluster.
-Finally, you will need to change your broadcast driver to `pusher` in your `.env` file:
+Next, you will need to change your broadcast driver to `pusher` in your `.env` file:
BROADCAST_DRIVER=pusher
+Finally, you are ready to install and configure [Laravel Echo](#client-side-installation), which will receive the broadcast events on the client-side.
+
#### Pusher Compatible Laravel Websockets
-The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) is a pure PHP, Pusher compatible websocket package for Laravel. This package allows you to leverage the full power of Laravel broadcasting without an external websocket provider or Node. For more information on installing and using this package, please consult its [official documentation](https://beyondco.de/docs/laravel-websockets).
+The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) package is a pure PHP, Pusher compatible WebSocket package for Laravel. This package allows you to leverage the full power of Laravel broadcasting without a commercial WebSocket provider. For more information on installing and using this package, please consult its [official documentation](https://beyondco.de/docs/laravel-websockets).
-
-#### Redis
+
+### Ably
-If you are using the Redis broadcaster, you should either install the phpredis PHP extension via PECL or install the Predis library via Composer:
+If you plan to broadcast your events using [Ably](https://ably.io), you should install the Ably PHP SDK using the Composer package manager:
- composer require predis/predis
+ composer require ably/ably-php
-Next, you should update your broadcast driver to `redis` in your `.env` file:
+Next, you should configure your Ably credentials in the `config/broadcasting.php` configuration file. An example Ably configuration is already included in this file, allowing you to quickly specify your key. Typically, this value should be set via the `ABLY_KEY` [environment variable](/docs/{{version}}/configuration#environment-configuration):
- BROADCAST_DRIVER=redis
+ ABLY_KEY=your-ably-key
-The Redis broadcaster will broadcast messages using Redis' pub / sub feature; however, you will need to pair this with a WebSocket server that can receive the messages from Redis and broadcast them to your WebSocket channels.
+Next, you will need to change your broadcast driver to `ably` in your `.env` file:
-When the Redis broadcaster publishes an event, it will be published on the event's specified channel names and the payload will be a JSON encoded string containing the event name, a `data` payload, and the user that generated the event's socket ID (if applicable).
+ BROADCAST_DRIVER=ably
-
-#### Socket.IO
+Finally, you are ready to install and configure [Laravel Echo](#client-side-installation), which will receive the broadcast events on the client-side.
-If you are going to pair the Redis broadcaster with a Socket.IO server, you will need to include the Socket.IO JavaScript client library in your application. You may install it via the NPM package manager:
+
+### Open Source Alternatives
- npm install --save-dev socket.io-client@2
+The [laravel-websockets](https://github.com/beyondcode/laravel-websockets) package is a pure PHP, Pusher compatible WebSocket package for Laravel. This package allows you to leverage the full power of Laravel broadcasting without a commercial WebSocket provider. For more information on installing and using this package, please consult its [official documentation](https://beyondco.de/docs/laravel-websockets).
-Next, you will need to instantiate Echo with the `socket.io` connector and a `host`.
+
+## Client Side Installation
- import Echo from "laravel-echo"
+
+### Pusher Channels
- window.io = require('socket.io-client');
+Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. You may install Echo via the NPM package manager. In this example, we will also install the `pusher-js` package since we will be using the Pusher Channels broadcaster:
- window.Echo = new Echo({
- broadcaster: 'socket.io',
- host: window.location.hostname + ':6001'
- });
+```bash
+npm install --save-dev laravel-echo pusher-js
+```
+
+Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. A great place to do this is at the bottom of the `resources/js/bootstrap.js` file that is included with the Laravel framework. By default, an example Echo configuration is already included in this file - you simply need to uncomment it:
-Finally, you will need to run a compatible Socket.IO server. Laravel does not include a Socket.IO server implementation; however, a community driven Socket.IO server is currently maintained at the [tlaverdure/laravel-echo-server](https://github.com/tlaverdure/laravel-echo-server) GitHub repository.
+```js
+import Echo from 'laravel-echo';
-
-#### Queue Prerequisites
+window.Pusher = require('pusher-js');
-Before broadcasting events, you will also need to configure and run a [queue listener](/docs/{{version}}/queues). All event broadcasting is done via queued jobs so that the response time of your application is not seriously affected.
+window.Echo = new Echo({
+ broadcaster: 'pusher',
+ key: process.env.MIX_PUSHER_APP_KEY,
+ cluster: process.env.MIX_PUSHER_APP_CLUSTER,
+ forceTLS: true
+});
+```
+
+Once you have uncommented and adjusted the Echo configuration according to your needs, you may compile your application's assets:
+
+ npm run dev
+
+> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Laravel Mix](/docs/{{version}}/mix).
+
+
+#### Using An Existing Client Instance
+
+If you already have a pre-configured Pusher Channels client instance that you would like Echo to utilize, you may pass it to Echo via the `client` configuration option:
+
+```js
+import Echo from 'laravel-echo';
+
+const client = require('pusher-js');
+
+window.Echo = new Echo({
+ broadcaster: 'pusher',
+ key: 'your-pusher-channels-key',
+ client: client
+});
+```
+
+
+### Ably
+
+Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by your server-side broadcasting driver. You may install Echo via the NPM package manager. In this example, we will also install the `pusher-js` package.
+
+You may wonder why we would install the `pusher-js` JavaScript library even though we are using Ably to broadcast our events. Thankfully, Ably includes a Pusher compatibility mode which lets us use the Pusher protocol when listening for events in our client-side application:
+
+```bash
+npm install --save-dev laravel-echo pusher-js
+```
+
+**Before continuing, you should enable Pusher protocol support in your Ably application settings. You may enable this feature within the "Protocol Adapter Settings" portion of your Ably application's settings dashboard.**
+
+Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. A great place to do this is at the bottom of the `resources/js/bootstrap.js` file that is included with the Laravel framework. By default, an example Echo configuration is already included in this file; however, the default configuration in the `bootstrap.js` file is intended for Pusher. You may copy the configuration below to transition your configuration to Ably:
+
+```js
+import Echo from 'laravel-echo';
+
+window.Pusher = require('pusher-js');
+
+window.Echo = new Echo({
+ broadcaster: 'pusher',
+ key: process.env.MIX_ABLY_PUBLIC_KEY,
+ wsHost: 'realtime-pusher.ably.io',
+ wsPort: 443,
+ disableStats: true,
+ encrypted: true,
+});
+```
+
+Note that our Ably Echo configuration references a `MIX_ABLY_PUBLIC_KEY` environment variable. This variable's value should be your Ably public key. Your public key is the portion of your Ably key that occurs before the `:` character.
+
+Once you have uncommented and adjusted the Echo configuration according to your needs, you may compile your application's assets:
+
+ npm run dev
+
+> {tip} To learn more about compiling your application's JavaScript assets, please consult the documentation on [Laravel Mix](/docs/{{version}}/mix).
## Concept Overview
-Laravel's event broadcasting allows you to broadcast your server-side Laravel events to your client-side JavaScript application using a driver-based approach to WebSockets. Currently, Laravel ships with [Pusher Channels](https://pusher.com/channels) and Redis drivers. The events may be easily consumed on the client-side using the [Laravel Echo](#installing-laravel-echo) Javascript package.
+Laravel's event broadcasting allows you to broadcast your server-side Laravel events to your client-side JavaScript application using a driver-based approach to WebSockets. Currently, Laravel ships with [Pusher Channels](https://pusher.com/channels) and [Ably](https://ably.io) drivers. The events may be easily consumed on the client-side using the [Laravel Echo](#installing-laravel-echo) JavaScript package.
Events are broadcast over "channels", which may be specified as public or private. Any visitor to your application may subscribe to a public channel without any authentication or authorization; however, in order to subscribe to a private channel, a user must be authenticated and authorized to listen on that channel.
@@ -143,21 +220,24 @@ Events are broadcast over "channels", which may be specified as public or privat
### Using An Example Application
-Before diving into each component of event broadcasting, let's take a high level overview using an e-commerce store as an example. We won't discuss the details of configuring [Pusher Channels](https://pusher.com/channels) or [Laravel Echo](#installing-laravel-echo) since that will be discussed in detail in other sections of this documentation.
+Before diving into each component of event broadcasting, let's take a high level overview using an e-commerce store as an example.
+
+In our application, let's assume we have a page that allows users to view the shipping status for their orders. Let's also assume that a `OrderShipmentStatusUpdated` event is fired when a shipping status update is processed by the application:
-In our application, let's assume we have a page that allows users to view the shipping status for their orders. Let's also assume that a `ShippingStatusUpdated` event is fired when a shipping status update is processed by the application:
+ use App\Events\OrderShipmentStatusUpdated;
- event(new ShippingStatusUpdated($update));
+ OrderShipmentStatusUpdated::dispatch($order);
#### The `ShouldBroadcast` Interface
-When a user is viewing one of their orders, we don't want them to have to refresh the page to view status updates. Instead, we want to broadcast the updates to the application as they are created. So, we need to mark the `ShippingStatusUpdated` event with the `ShouldBroadcast` interface. This will instruct Laravel to broadcast the event when it is fired:
+When a user is viewing one of their orders, we don't want them to have to refresh the page to view status updates. Instead, we want to broadcast the updates to the application as they are created. So, we need to mark the `OrderShipmentStatusUpdated` event with the `ShouldBroadcast` interface. This will instruct Laravel to broadcast the event when it is fired:
update->order_id);
+ return new PrivateChannel('orders.'.$this->order->id);
}
#### Authorizing Channels
-Remember, users must be authorized to listen on private channels. We may define our channel authorization rules in the `routes/channels.php` file. In this example, we need to verify that any user attempting to listen on the private `order.1` channel is actually the creator of the order:
+Remember, users must be authorized to listen on private channels. We may define our channel authorization rules in our application's `routes/channels.php` file. In this example, we need to verify that any user attempting to listen on the private `order.1` channel is actually the creator of the order:
- Broadcast::channel('order.{orderId}', function ($user, $orderId) {
+ use App\Models\Order;
+
+ Broadcast::channel('orders.{orderId}', function ($user, $orderId) {
return $user->id === Order::findOrNew($orderId)->user_id;
});
@@ -203,17 +285,19 @@ All authorization callbacks receive the currently authenticated user as their fi
#### Listening For Event Broadcasts
-Next, all that remains is to listen for the event in our JavaScript application. We can do this using Laravel Echo. First, we'll use the `private` method to subscribe to the private channel. Then, we may use the `listen` method to listen for the `ShippingStatusUpdated` event. By default, all of the event's public properties will be included on the broadcast event:
+Next, all that remains is to listen for the event in our JavaScript application. We can do this using Laravel Echo. First, we'll use the `private` method to subscribe to the private channel. Then, we may use the `listen` method to listen for the `OrderShipmentStatusUpdated` event. By default, all of the event's public properties will be included on the broadcast event:
- Echo.private(`order.${orderId}`)
- .listen('ShippingStatusUpdated', (e) => {
- console.log(e.update);
- });
+```js
+Echo.private(`orders.${orderId}`)
+ .listen('OrderShipmentStatusUpdated', (e) => {
+ console.log(e.order);
+ });
+```
## Defining Broadcast Events
-To inform Laravel that a given event should be broadcast, implement the `Illuminate\Contracts\Broadcasting\ShouldBroadcast` interface on the event class. This interface is already imported into all event classes generated by the framework so you may easily add it to any of your events.
+To inform Laravel that a given event should be broadcast, you must implement the `Illuminate\Contracts\Broadcasting\ShouldBroadcast` interface on the event class. This interface is already imported into all event classes generated by the framework so you may easily add it to any of your events.
The `ShouldBroadcast` interface requires you to implement a single method: `broadcastOn`. The `broadcastOn` method should return a channel or array of channels that the event should broadcast on. The channels should be instances of `Channel`, `PrivateChannel`, or `PresenceChannel`. Instances of `Channel` represent public channels that any user may subscribe to, while `PrivateChannels` and `PresenceChannels` represent private channels that require [channel authorization](#authorizing-channels):
@@ -233,11 +317,17 @@ The `ShouldBroadcast` interface requires you to implement a single method: `broa
{
use SerializesModels;
+ /**
+ * The user that created the server.
+ *
+ * @var \App\Models\User
+ */
public $user;
/**
* Create a new event instance.
*
+ * @param \App\Models\User $user
* @return void
*/
public function __construct(User $user)
@@ -256,7 +346,7 @@ The `ShouldBroadcast` interface requires you to implement a single method: `broa
}
}
-Then, you only need to [fire the event](/docs/{{version}}/events) as you normally would. Once the event has been fired, a [queued job](/docs/{{version}}/queues) will automatically broadcast the event over your specified broadcast driver.
+After implementing the `ShouldBroadcast` interface, you only need to [fire the event](/docs/{{version}}/events) as you normally would. Once the event has been fired, a [queued job](/docs/{{version}}/queues) will automatically broadcast the event using your specified broadcast driver.
### Broadcast Name
@@ -307,14 +397,21 @@ However, if you wish to have more fine-grained control over your broadcast paylo
### Broadcast Queue
-By default, each broadcast event is placed on the default queue for the default queue connection specified in your `queue.php` configuration file. You may customize the queue used by the broadcaster by defining a `broadcastQueue` property on your event class. This property should specify the name of the queue you wish to use when broadcasting:
+By default, each broadcast event is placed on the default queue for the default queue connection specified in your `queue.php` configuration file. You may customize the queue connection and name used by the broadcaster by defining `connection` and `queue` properties on your event class:
+
+ /**
+ * The name of the queue connection to use when broadcasting the event.
+ *
+ * @var string
+ */
+ public $connection = 'redis';
/**
- * The name of the queue on which to place the event.
+ * The name of the queue on which to place the broadcasting job.
*
* @var string
*/
- public $broadcastQueue = 'your-queue-name';
+ public $queue = 'default';
If you want to broadcast your event using the `sync` queue instead of the default queue driver, you can implement the `ShouldBroadcastNow` interface instead of `ShouldBroadcast`:
@@ -322,10 +419,11 @@ If you want to broadcast your event using the `sync` queue instead of the defaul
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
- class ShippingStatusUpdated implements ShouldBroadcastNow
+ class OrderShipmentStatusUpdated implements ShouldBroadcastNow
{
//
}
+
### Broadcast Conditions
@@ -338,7 +436,7 @@ Sometimes you want to broadcast your event only if a given condition is true. Yo
*/
public function broadcastWhen()
{
- return $this->value > 100;
+ return $this->order->value > 100;
}
@@ -349,7 +447,7 @@ Private channels require you to authorize that the currently authenticated user
### Defining Authorization Routes
-Thankfully, Laravel makes it easy to define the routes to respond to channel authorization requests. In the `BroadcastServiceProvider` included with your Laravel application, you will see a call to the `Broadcast::routes` method. This method will register the `/broadcasting/auth` route to handle authorization requests:
+Thankfully, Laravel makes it easy to define the routes to respond to channel authorization requests. In the `App\Providers\BroadcastServiceProvider` included with your Laravel application, you will see a call to the `Broadcast::routes` method. This method will register the `/broadcasting/auth` route to handle authorization requests:
Broadcast::routes();
@@ -364,16 +462,16 @@ By default, Echo will use the `/broadcasting/auth` endpoint to authorize channel
window.Echo = new Echo({
broadcaster: 'pusher',
- key: 'your-pusher-channels-key',
+ // ...
authEndpoint: '/custom/endpoint/auth'
});
### Defining Authorization Callbacks
-Next, we need to define the logic that will actually perform the channel authorization. This is done in the `routes/channels.php` file that is included with your application. In this file, you may use the `Broadcast::channel` method to register channel authorization callbacks:
+Next, we need to define the logic that will actually determine if the currently authenticated user can listen to a given channel. This is done in the `routes/channels.php` file that is included with your application. In this file, you may use the `Broadcast::channel` method to register channel authorization callbacks:
- Broadcast::channel('order.{orderId}', function ($user, $orderId) {
+ Broadcast::channel('orders.{orderId}', function ($user, $orderId) {
return $user->id === Order::findOrNew($orderId)->user_id;
});
@@ -384,14 +482,16 @@ All authorization callbacks receive the currently authenticated user as their fi
#### Authorization Callback Model Binding
-Just like HTTP routes, channel routes may also take advantage of implicit and explicit [route model binding](/docs/{{version}}/routing#route-model-binding). For example, instead of receiving the string or numeric order ID, you may request an actual `Order` model instance:
+Just like HTTP routes, channel routes may also take advantage of implicit and explicit [route model binding](/docs/{{version}}/routing#route-model-binding). For example, instead of receiving a string or numeric order ID, you may request an actual `Order` model instance:
use App\Models\Order;
- Broadcast::channel('order.{order}', function ($user, Order $order) {
+ Broadcast::channel('orders.{order}', function ($user, Order $order) {
return $user->id === $order->user_id;
});
+> {note} Unlike HTTP route model binding, channel model binding does not support automatic [implicit model binding scoping](/docs/{{version}}/routing#implicit-model-binding-scoping). However, this is rarely a problem because most channels can be scoped based on a single model's unique, primary key.
+
#### Authorization Callback Authentication
@@ -404,7 +504,7 @@ Private and presence broadcast channels authenticate the current user via your a
### Defining Channel Classes
-If your application is consuming many different channels, your `routes/channels.php` file could become bulky. So, instead of using Closures to authorize channels, you may use channel classes. To generate a channel class, use the `make:channel` Artisan command. This command will place a new channel class in the `App/Broadcasting` directory.
+If your application is consuming many different channels, your `routes/channels.php` file could become bulky. So, instead of using closures to authorize channels, you may use channel classes. To generate a channel class, use the `make:channel` Artisan command. This command will place a new channel class in the `App/Broadcasting` directory.
php artisan make:channel OrderChannel
@@ -412,9 +512,9 @@ Next, register your channel in your `routes/channels.php` file:
use App\Broadcasting\OrderChannel;
- Broadcast::channel('order.{order}', OrderChannel::class);
+ Broadcast::channel('orders.{order}', OrderChannel::class);
-Finally, you may place the authorization logic for your channel in the channel class' `join` method. This `join` method will house the same logic you would have typically placed in your channel authorization Closure. You may also take advantage of channel model binding:
+Finally, you may place the authorization logic for your channel in the channel class' `join` method. This `join` method will house the same logic you would have typically placed in your channel authorization closure. You may also take advantage of channel model binding:
## Broadcasting Events
-Once you have defined an event and marked it with the `ShouldBroadcast` interface, you only need to fire the event using the `event` function. The event dispatcher will notice that the event is marked with the `ShouldBroadcast` interface and will queue the event for broadcasting:
+Once you have defined an event and marked it with the `ShouldBroadcast` interface, you only need to fire the event using the event's dispatch method. The event dispatcher will notice that the event is marked with the `ShouldBroadcast` interface and will queue the event for broadcasting:
+
+ use App\Events\OrderShipmentStatusUpdated;
- event(new ShippingStatusUpdated($update));
+ OrderShipmentStatusUpdated::dispatch($order));
### Only To Others
-When building an application that utilizes event broadcasting, you may substitute the `event` function with the `broadcast` function. Like the `event` function, the `broadcast` function dispatches the event to your server-side listeners:
+When building an application that utilizes event broadcasting, you may occasionally need to broadcast an event to all subscribers to a given channel except for the current user. You may accomplish this using the `broadcast` helper and the `toOthers` method:
- broadcast(new ShippingStatusUpdated($update));
+ use App\Events\OrderShipmentStatusUpdated;
-However, the `broadcast` function also exposes the `toOthers` method which allows you to exclude the current user from the broadcast's recipients:
+ broadcast(new OrderShipmentStatusUpdated($update))->toOthers();
- broadcast(new ShippingStatusUpdated($update))->toOthers();
-
-To better understand when you may want to use the `toOthers` method, let's imagine a task list application where a user may create a new task by entering a task name. To create a task, your application might make a request to a `/task` end-point which broadcasts the task's creation and returns a JSON representation of the new task. When your JavaScript application receives the response from the end-point, it might directly insert the new task into its task list like so:
+To better understand when you may want to use the `toOthers` method, let's imagine a task list application where a user may create a new task by entering a task name. To create a task, your application might make a request to a `/task` URL which broadcasts the task's creation and returns a JSON representation of the new task. When your JavaScript application receives the response from the end-point, it might directly insert the new task into its task list like so:
axios.post('/task', task)
.then((response) => {
this.tasks.push(response.data);
});
-However, remember that we also broadcast the task's creation. If your JavaScript application is listening for this event in order to add tasks to the task list, you will have duplicate tasks in your list: one from the end-point and one from the broadcast. You may solve this by using the `toOthers` method to instruct the broadcaster to not broadcast the event to the current user.
+However, remember that we also broadcast the task's creation. If your JavaScript application is also listening for this event in order to add tasks to the task list, you will have duplicate tasks in your list: one from the end-point and one from the broadcast. You may solve this by using the `toOthers` method to instruct the broadcaster to not broadcast the event to the current user.
> {note} Your event must use the `Illuminate\Broadcasting\InteractsWithSockets` trait in order to call the `toOthers` method.
#### Configuration
-When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. If you are using [Vue](https://vuejs.org) and [Axios](https://github.com/mzabriskie/axios), the socket ID will automatically be attached to every outgoing request as a `X-Socket-ID` header. Then, when you call the `toOthers` method, Laravel will extract the socket ID from the header and instruct the broadcaster to not broadcast to any connections with that socket ID.
+When you initialize a Laravel Echo instance, a socket ID is assigned to the connection. If you are using a global [Axios](https://github.com/mzabriskie/axios) instance to make HTTP requests from your JavaScript application, the socket ID will automatically be attached to every outgoing request as a `X-Socket-ID` header. Then, when you call the `toOthers` method, Laravel will extract the socket ID from the header and instruct the broadcaster to not broadcast to any connections with that socket ID.
-If you are not using Vue and Axios, you will need to manually configure your JavaScript application to send the `X-Socket-ID` header. You may retrieve the socket ID using the `Echo.socketId` method:
+If you are not using a global Axios instance, you will need to manually configure your JavaScript application to send the `X-Socket-ID` header with all outgoing requests. You may retrieve the socket ID using the `Echo.socketId` method:
var socketId = Echo.socketId();
## Receiving Broadcasts
-
-### Installing Laravel Echo
-
-Laravel Echo is a JavaScript library that makes it painless to subscribe to channels and listen for events broadcast by Laravel. You may install Echo via the NPM package manager. In this example, we will also install the `pusher-js` package since we will be using the Pusher Channels broadcaster:
-
- npm install --save-dev laravel-echo pusher-js
-
-Once Echo is installed, you are ready to create a fresh Echo instance in your application's JavaScript. A great place to do this is at the bottom of the `resources/js/bootstrap.js` file that is included with the Laravel framework:
-
- import Echo from "laravel-echo"
-
- window.Echo = new Echo({
- broadcaster: 'pusher',
- key: 'your-pusher-channels-key'
- });
-
-When creating an Echo instance that uses the `pusher` connector, you may also specify a `cluster` as well as whether the connection must be made over TLS (by default, when `forceTLS` is `false`, a non-TLS connection will be made if the page was loaded over HTTP, or as a fallback if a TLS connection fails):
-
- window.Echo = new Echo({
- broadcaster: 'pusher',
- key: 'your-pusher-channels-key',
- cluster: 'eu',
- forceTLS: true
- });
-
-
-#### Using An Existing Client Instance
-
-If you already have a Pusher Channels or Socket.io client instance that you would like Echo to utilize, you may pass it to Echo via the `client` configuration option:
-
- const client = require('pusher-js');
-
- window.Echo = new Echo({
- broadcaster: 'pusher',
- key: 'your-pusher-channels-key',
- client: client
- });
-
### Listening For Events
-Once you have installed and instantiated Echo, you are ready to start listening for event broadcasts. First, use the `channel` method to retrieve an instance of a channel, then call the `listen` method to listen for a specified event:
+Once you have [installed and instantiated Laravel Echo](#client-side-installation), you are ready to start listening for events that are broadcast from your Laravel application. First, use the `channel` method to retrieve an instance of a channel, then call the `listen` method to listen for a specified event:
- Echo.channel('orders')
- .listen('OrderShipped', (e) => {
- console.log(e.order.name);
- });
+```js
+Echo.channel(`orders.${this.order.id}`)
+ .listen('OrderShipmentStatusUpdated', (e) => {
+ console.log(e.order.name);
+ });
+```
If you would like to listen for events on a private channel, use the `private` method instead. You may continue to chain calls to the `listen` method to listen for multiple events on a single channel:
- Echo.private('orders')
- .listen(...)
- .listen(...)
- .listen(...);
+```js
+Echo.private(`orders.${this.order.id}`)
+ .listen(...)
+ .listen(...)
+ .listen(...);
+```
### Leaving A Channel
To leave a channel, you may call the `leaveChannel` method on your Echo instance:
- Echo.leaveChannel('orders');
+```js
+Echo.leaveChannel(`orders.${this.order.id}`);
+```
If you would like to leave a channel and also its associated private and presence channels, you may call the `leave` method:
- Echo.leave('orders');
-
+```js
+Echo.leave(`orders.${this.order.id}`);
+```
### Namespaces
-You may have noticed in the examples above that we did not specify the full namespace for the event classes. This is because Echo will automatically assume the events are located in the `App\Events` namespace. However, you may configure the root namespace when you instantiate Echo by passing a `namespace` configuration option:
+You may have noticed in the examples above that we did not specify the full `App\Events` namespace for the event classes. This is because Echo will automatically assume the events are located in the `App\Events` namespace. However, you may configure the root namespace when you instantiate Echo by passing a `namespace` configuration option:
- window.Echo = new Echo({
- broadcaster: 'pusher',
- key: 'your-pusher-channels-key',
- namespace: 'App.Other.Namespace'
- });
+```js
+window.Echo = new Echo({
+ broadcaster: 'pusher',
+ // ...
+ namespace: 'App.Other.Namespace'
+});
+```
Alternatively, you may prefix event classes with a `.` when subscribing to them using Echo. This will allow you to always specify the fully-qualified class name:
- Echo.channel('orders')
- .listen('.Namespace\\Event\\Class', (e) => {
- //
- });
+```js
+Echo.channel('orders')
+ .listen('.Namespace\\Event\\Class', (e) => {
+ //
+ });
+```
## Presence Channels
-Presence channels build on the security of private channels while exposing the additional feature of awareness of who is subscribed to the channel. This makes it easy to build powerful, collaborative application features such as notifying users when another user is viewing the same page.
+Presence channels build on the security of private channels while exposing the additional feature of awareness of who is subscribed to the channel. This makes it easy to build powerful, collaborative application features such as notifying users when another user is viewing the same page or listing the inhabitants of a chat room.
### Authorizing Presence Channels
@@ -626,13 +699,13 @@ Presence channels may receive events just like public or private channels. Using
return new PresenceChannel('room.'.$this->message->room_id);
}
-Like public or private events, presence channel events may be broadcast using the `broadcast` function. As with other events, you may use the `toOthers` method to exclude the current user from receiving the broadcast:
+As with other events, you may use the `broadcast` helper and the `toOthers` method to exclude the current user from receiving the broadcast:
broadcast(new NewMessage($message));
broadcast(new NewMessage($message))->toOthers();
-You may listen for the join event via Echo's `listen` method:
+As typical of other types of events, you may listen for events sent to presence channels using Echo's `listen` method:
Echo.join(`chat.${roomId}`)
.here(...)
@@ -651,14 +724,14 @@ Sometimes you may wish to broadcast an event to other connected clients without
To broadcast client events, you may use Echo's `whisper` method:
- Echo.private('chat')
+ Echo.private(`chat.${roomId}`)
.whisper('typing', {
name: this.user.name
});
To listen for client events, you may use the `listenForWhisper` method:
- Echo.private('chat')
+ Echo.private(`chat.${roomId}`)
.listenForWhisper('typing', (e) => {
console.log(e.name);
});
@@ -666,7 +739,7 @@ To listen for client events, you may use the `listenForWhisper` method:
## Notifications
-By pairing event broadcasting with [notifications](/docs/{{version}}/notifications), your JavaScript application may receive new notifications as they occur without needing to refresh the page. First, be sure to read over the documentation on using [the broadcast notification channel](/docs/{{version}}/notifications#broadcast-notifications).
+By pairing event broadcasting with [notifications](/docs/{{version}}/notifications), your JavaScript application may receive new notifications as they occur without needing to refresh the page. Before getting started, be sure to read over the documentation on using [the broadcast notification channel](/docs/{{version}}/notifications#broadcast-notifications).
Once you have configured a notification to use the broadcast channel, you may listen for the broadcast events using Echo's `notification` method. Remember, the channel name should match the class name of the entity receiving the notifications:
diff --git a/cache.md b/cache.md
index 54d1d967892..180d55eaee0 100644
--- a/cache.md
+++ b/cache.md
@@ -1,5 +1,6 @@
# Cache
+- [Introduction](#introduction)
- [Configuration](#configuration)
- [Driver Prerequisites](#driver-prerequisites)
- [Cache Usage](#cache-usage)
@@ -21,12 +22,19 @@
- [Registering The Driver](#registering-the-driver)
- [Events](#events)
+
+## Introduction
+
+Some of the data retrieval or processing tasks performed by your application could be CPU intensive or take several seconds to complete. When this is the case, it is common to cache the retrieved data for a time so it can retrieved quickly on subsequent requests for the same data. The cached data is usually stored in a very fast data store such as [Memcached](https://memcached.org) or [Redis](https://redis.io).
+
+Thankfully, Laravel provides an expressive, unified API for various cache backends, allowing you to take advantage of their blazing fast data retrieval and speed up your web application.
+
## Configuration
-Laravel provides an expressive, unified API for various caching backends. The cache configuration is located at `config/cache.php`. In this file you may specify which cache driver you would like to be used by default throughout your application. Laravel supports popular caching backends like [Memcached](https://memcached.org), [Redis](https://redis.io), and [DynamoDB](https://aws.amazon.com/dynamodb) out of the box. Laravel also supports an APC, Array, Database, File, and `null` cache driver.
+Your application's cache configuration file is located at `config/cache.php`. In this file you may specify which cache driver you would like to be used by default throughout your application. Laravel supports popular caching backends like [Memcached](https://memcached.org), [Redis](https://redis.io), [DynamoDB](https://aws.amazon.com/dynamodb), and relational databases out of the box. In addition, a file based cache driver is available, while `array` and "null" cache drivers provide convenient cache backends for your automated tests.
-The cache configuration file also contains various other options, which are documented within the file, so make sure to read over these options. By default, Laravel is configured to use the `file` cache driver, which stores the serialized, cached objects in the filesystem. For larger applications, it is recommended that you use a more robust driver such as Memcached or Redis. You may even configure multiple cache configurations for the same driver.
+The cache configuration file also contains various other options, which are documented within the file, so make sure to read over these options. By default, Laravel is configured to use the `file` cache driver, which stores the serialized, cached objects on the server's filesystem. For larger applications, it is recommended that you use a more robust driver such as Memcached or Redis. You may even configure multiple cache configurations for the same driver.
### Driver Prerequisites
@@ -47,17 +55,19 @@ When using the `database` cache driver, you will need to setup a table to contai
#### Memcached
-Using the Memcached driver requires the [Memcached PECL package](https://pecl.php.net/package/memcached) to be installed. You may list all of your Memcached servers in the `config/cache.php` configuration file:
+Using the Memcached driver requires the [Memcached PECL package](https://pecl.php.net/package/memcached) to be installed. You may list all of your Memcached servers in the `config/cache.php` configuration file. This file already contains a `memcached.servers` entry to get you started:
'memcached' => [
- [
- 'host' => '127.0.0.1',
- 'port' => 11211,
- 'weight' => 100
+ 'servers' => [
+ [
+ 'host' => env('MEMCACHED_HOST', '127.0.0.1'),
+ 'port' => env('MEMCACHED_PORT', 11211),
+ 'weight' => 100,
+ ],
],
],
-You may also set the `host` option to a UNIX socket path. If you do this, the `port` option should be set to `0`:
+If needed, you may set the `host` option to a UNIX socket path. If you do this, the `port` option should be set to `0`:
'memcached' => [
[
@@ -70,7 +80,7 @@ You may also set the `host` option to a UNIX socket path. If you do this, the `p
#### Redis
-Before using a Redis cache with Laravel, you will need to either install the PhpRedis PHP extension via PECL or install the `predis/predis` package (~1.0) via Composer.
+Before using a Redis cache with Laravel, you will need to either install the PhpRedis PHP extension via PECL or install the `predis/predis` package (~1.0) via Composer. [Laravel Sail](/docs/{{version}}/sail) already includes this extension. In addition, official Laravel deployment platforms such as [Laravel Forge](https://forge.laravel.com) and [Laravel Vapor](https://vapor.laravel.com) have the PhpRedis extension installed by default.
For more information on configuring Redis, consult its [Laravel documentation page](/docs/{{version}}/redis#configuration).
@@ -80,9 +90,7 @@ For more information on configuring Redis, consult its [Laravel documentation pa
### Obtaining A Cache Instance
-The `Illuminate\Contracts\Cache\Factory` and `Illuminate\Contracts\Cache\Repository` [contracts](/docs/{{version}}/contracts) provide access to Laravel's cache services. The `Factory` contract provides access to all cache drivers defined for your application. The `Repository` contract is typically an implementation of the default cache driver for your application as specified by your `cache` configuration file.
-
-However, you may also use the `Cache` facade, which is what we will use throughout this documentation. The `Cache` facade provides convenient, terse access to the underlying implementations of the Laravel cache contracts:
+To obtain a cache store instance, you may use the `Cache` facade, which is what we will use throughout this documentation. The `Cache` facade provides convenient, terse access to the underlying implementations of the Laravel cache contracts:
### Retrieving Items From The Cache
-The `get` method on the `Cache` facade is used to retrieve items from the cache. If the item does not exist in the cache, `null` will be returned. If you wish, you may pass a second argument to the `get` method specifying the default value you wish to be returned if the item doesn't exist:
+The `Cache` facade's `get` method is used to retrieve items from the cache. If the item does not exist in the cache, `null` will be returned. If you wish, you may pass a second argument to the `get` method specifying the default value you wish to be returned if the item doesn't exist:
$value = Cache::get('key');
$value = Cache::get('key', 'default');
-You may even pass a `Closure` as the default value. The result of the `Closure` will be returned if the specified item does not exist in the cache. Passing a Closure allows you to defer the retrieval of default values from a database or other external service:
+You may even pass a closure as the default value. The result of the closure will be returned if the specified item does not exist in the cache. Passing a closure allows you to defer the retrieval of default values from a database or other external service:
$value = Cache::get('key', function () {
return DB::table(...)->get();
@@ -132,7 +140,7 @@ You may even pass a `Closure` as the default value. The result of the `Closure`
#### Checking For Item Existence
-The `has` method may be used to determine if an item exists in the cache. This method will return `false` if the value is `null`:
+The `has` method may be used to determine if an item exists in the cache. This method will also return `false` if the item exists but its value is `null`:
if (Cache::has('key')) {
//
@@ -157,9 +165,9 @@ Sometimes you may wish to retrieve an item from the cache, but also store a defa
return DB::table('users')->get();
});
-If the item does not exist in the cache, the `Closure` passed to the `remember` method will be executed and its result will be placed in the cache.
+If the item does not exist in the cache, the closure passed to the `remember` method will be executed and its result will be placed in the cache.
-You may use the `rememberForever` method to retrieve an item from the cache or store it forever:
+You may use the `rememberForever` method to retrieve an item from the cache or store it forever if it does not exist:
$value = Cache::rememberForever('users', function () {
return DB::table('users')->get();
@@ -177,20 +185,20 @@ If you need to retrieve an item from the cache and then delete the item, you may
You may use the `put` method on the `Cache` facade to store items in the cache:
- Cache::put('key', 'value', $seconds);
+ Cache::put('key', 'value', $seconds = 10);
If the storage time is not passed to the `put` method, the item will be stored indefinitely:
Cache::put('key', 'value');
-Instead of passing the number of seconds as an integer, you may also pass a `DateTime` instance representing the expiration time of the cached item:
+Instead of passing the number of seconds as an integer, you may also pass a `DateTime` instance representing the desired expiration time of the cached item:
Cache::put('key', 'value', now()->addMinutes(10));
#### Store If Not Present
-The `add` method will only add the item to the cache if it does not already exist in the cache store. The method will return `true` if the item is actually added to the cache. Otherwise, the method will return `false`:
+The `add` method will only add the item to the cache if it does not already exist in the cache store. The method will return `true` if the item is actually added to the cache. Otherwise, the method will return `false`. The `add` method is an atomic operation:
Cache::add('key', 'value', $seconds);
@@ -210,7 +218,7 @@ You may remove items from the cache using the `forget` method:
Cache::forget('key');
-You may also remove items by providing a zero or negative TTL:
+You may also remove items by providing a zero or negative number of expiration seconds:
Cache::put('key', 'value', 0);
@@ -220,12 +228,12 @@ You may clear the entire cache using the `flush` method:
Cache::flush();
-> {note} Flushing the cache does not respect the cache prefix and will remove all entries from the cache. Consider this carefully when clearing a cache which is shared by other applications.
+> {note} Flushing the cache does not respect your configured cache "prefix" and will remove all entries from the cache. Consider this carefully when clearing a cache which is shared by other applications.
### The Cache Helper
-In addition to using the `Cache` facade or [cache contract](/docs/{{version}}/contracts), you may also use the global `cache` function to retrieve and store data via the cache. When the `cache` function is called with a single, string argument, it will return the value of the given key:
+In addition to using the `Cache` facade, you may also use the global `cache` function to retrieve and store data via the cache. When the `cache` function is called with a single, string argument, it will return the value of the given key:
$value = cache('key');
@@ -241,7 +249,7 @@ When the `cache` function is called without any arguments, it returns an instanc
return DB::table('users')->get();
});
-> {tip} When testing call to the global `cache` function, you may use the `Cache::shouldReceive` method just as if you were [testing a facade](/docs/{{version}}/mocking#mocking-facades).
+> {tip} When testing call to the global `cache` function, you may use the `Cache::shouldReceive` method just as if you were [testing the facade](/docs/{{version}}/mocking#mocking-facades).
## Cache Tags
@@ -251,7 +259,7 @@ When the `cache` function is called without any arguments, it returns an instanc
### Storing Tagged Cache Items
-Cache tags allow you to tag related items in the cache and then flush all cached values that have been assigned a given tag. You may access a tagged cache by passing in an ordered array of tag names. For example, let's access a tagged cache and `put` value in the cache:
+Cache tags allow you to tag related items in the cache and then flush all cached values that have been assigned a given tag. You may access a tagged cache by passing in an ordered array of tag names. For example, let's access a tagged cache and `put` a value into the cache:
Cache::tags(['people', 'artists'])->put('John', $john, $seconds);
@@ -273,7 +281,7 @@ You may flush all items that are assigned a tag or list of tags. For example, th
Cache::tags(['people', 'authors'])->flush();
-In contrast, this statement would remove only caches tagged with `authors`, so `Anne` would be removed, but not `John`:
+In contrast, this statement would remove only cached values tagged with `authors`, so `Anne` would be removed, but not `John`:
Cache::tags('authors')->flush();
@@ -288,7 +296,7 @@ In contrast, this statement would remove only caches tagged with `authors`, so `
#### Database
-When using the `database` cache driver, you will need to setup a table to contain the cache locks. You'll find an example `Schema` declaration for the table below:
+When using the `database` cache driver, you will need to setup a table to contain your application's cache locks. You'll find an example `Schema` declaration for the table below:
Schema::create('cache_locks', function ($table) {
$table->string('key')->primary();
@@ -311,7 +319,7 @@ Atomic locks allow for the manipulation of distributed locks without worrying ab
$lock->release();
}
-The `get` method also accepts a Closure. After the Closure is executed, Laravel will automatically release the lock:
+The `get` method also accepts a closure. After the closure is executed, Laravel will automatically release the lock:
Cache::lock('foo')->get(function () {
// Lock acquired indefinitely and automatically released...
@@ -333,6 +341,8 @@ If the lock is not available at the moment you request it, you may instruct Lara
optional($lock)->release();
}
+The example above may be simplified by passing a closure to the `block` method. When a closure is passed to this method, Laravel will attempt to acquire the lock for the specified number of seconds and will automatically release the lock once the closure has been executed:
+
Cache::lock('foo', 10)->block(5, function () {
// Lock acquired after waiting maximum of 5 seconds...
});
@@ -340,23 +350,25 @@ If the lock is not available at the moment you request it, you may instruct Lara
### Managing Locks Across Processes
-Sometimes, you may wish to acquire a lock in one process and release it in another process. For example, you may acquire a lock during a web request and wish to release the lock at the end of a queued job that is triggered by that request. In this scenario, you should pass the lock's scoped "owner token" to the queued job so that the job can re-instantiate the lock using the given token:
+Sometimes, you may wish to acquire a lock in one process and release it in another process. For example, you may acquire a lock during a web request and wish to release the lock at the end of a queued job that is triggered by that request. In this scenario, you should pass the lock's scoped "owner token" to the queued job so that the job can re-instantiate the lock using the given token.
+
+In the example below, we will dispatch a queued job if a lock is successfully acquired. In addition, we will pass the lock's owner token to the queued job via the lock's `owner` method:
- // Within Controller...
$podcast = Podcast::find($id);
- $lock = Cache::lock('foo', 120);
+ $lock = Cache::lock('processing', 120);
if ($result = $lock->get()) {
ProcessPodcast::dispatch($podcast, $lock->owner());
}
- // Within ProcessPodcast Job...
- Cache::restoreLock('foo', $this->owner)->release();
+Within our application's `ProcessPodcast` job, we can restore and release the lock using the owner token:
+
+ Cache::restoreLock('processing', $this->owner)->release();
If you would like to release a lock without respecting its current owner, you may use the `forceRelease` method:
- Cache::lock('foo')->forceRelease();
+ Cache::lock('processing')->forceRelease();
## Adding Custom Cache Drivers
@@ -364,7 +376,7 @@ If you would like to release a lock without respecting its current owner, you ma
### Writing The Driver
-To create our custom cache driver, we first need to implement the `Illuminate\Contracts\Cache\Store` [contract](/docs/{{version}}/contracts). So, a MongoDB cache implementation would look something like this:
+To create our custom cache driver, we first need to implement the `Illuminate\Contracts\Cache\Store` [contract](/docs/{{version}}/contracts). So, a MongoDB cache implementation might look something like this:
### Registering The Driver
-To register the custom cache driver with Laravel, we will use the `extend` method on the `Cache` facade. The call to `Cache::extend` could be done in the `boot` method of the default `App\Providers\AppServiceProvider` that ships with fresh Laravel applications, or you may create your own service provider to house the extension - just don't forget to register the provider in the `config/app.php` provider array:
+To register the custom cache driver with Laravel, we will use the `extend` method on the `Cache` facade. Since other service providers may attempt to read cached values within their `boot` method, we will register our custom driver within a `booting` callback. By using the `booting` callback, we can ensure that the custom driver is registered just before the `boot` method is called on our application's service providers but after the `register` method is called on all of the service providers. We will register our `booting` callback within the `register` method of our application's `App\Providers\AppServiceProvider` class:
app->booting(function () {
+ Cache::extend('mongo', function ($app) {
+ return Cache::repository(new MongoStore);
+ });
+ });
}
/**
@@ -426,20 +442,18 @@ To register the custom cache driver with Laravel, we will use the `extend` metho
*/
public function boot()
{
- Cache::extend('mongo', function ($app) {
- return Cache::repository(new MongoStore);
- });
+ //
}
}
-The first argument passed to the `extend` method is the name of the driver. This will correspond to your `driver` option in the `config/cache.php` configuration file. The second argument is a Closure that should return an `Illuminate\Cache\Repository` instance. The Closure will be passed an `$app` instance, which is an instance of the [service container](/docs/{{version}}/container).
+The first argument passed to the `extend` method is the name of the driver. This will correspond to your `driver` option in the `config/cache.php` configuration file. The second argument is a closure that should return an `Illuminate\Cache\Repository` instance. The closure will be passed an `$app` instance, which is an instance of the [service container](/docs/{{version}}/container).
Once your extension is registered, update your `config/cache.php` configuration file's `driver` option to the name of your extension.
## Events
-To execute code on every cache operation, you may listen for the [events](/docs/{{version}}/events) fired by the cache. Typically, you should place these event listeners within your `EventServiceProvider`:
+To execute code on every cache operation, you may listen for the [events](/docs/{{version}}/events) fired by the cache. Typically, you should place these event listeners within your application's `App\Providers\EventServiceProvider` class:
/**
* The event listener mappings for the application.
diff --git a/cashier-paddle.md b/cashier-paddle.md
index 0fb7b451bf4..7dc79647a8a 100644
--- a/cashier-paddle.md
+++ b/cashier-paddle.md
@@ -1,8 +1,9 @@
-# Laravel Cashier Paddle
+# Laravel Cashier (Paddle)
- [Introduction](#introduction)
- [Upgrading Cashier](#upgrading-cashier)
- [Installation](#installation)
+ - [Database Migrations](#database-migrations)
- [Configuration](#configuration)
- [Billable Model](#billable-model)
- [API Keys](#api-keys)
@@ -29,7 +30,6 @@
- [Without Payment Method Up Front](#without-payment-method-up-front)
- [Handling Paddle Webhooks](#handling-paddle-webhooks)
- [Defining Webhook Event Handlers](#defining-webhook-event-handlers)
- - [Failed Subscriptions](#handling-failed-subscriptions)
- [Verifying Webhook Signatures](#verifying-webhook-signatures)
- [Single Charges](#single-charges)
- [Simple Charge](#simple-charge)
@@ -45,7 +45,7 @@
Laravel Cashier Paddle provides an expressive, fluent interface to [Paddle's](https://paddle.com) subscription billing services. It handles almost all of the boilerplate subscription billing code you are dreading. In addition to basic subscription management, Cashier can handle: coupons, swapping subscription, subscription "quantities", cancellation grace periods, and more.
-While working with Cashier we recommend you also refer to Paddle's [user guides](https://developer.paddle.com/guides) and [API documentation](https://developer.paddle.com/api-reference/intro).
+While working with Cashier we recommend you also review Paddle's [user guides](https://developer.paddle.com/guides) and [API documentation](https://developer.paddle.com/api-reference/intro).
## Upgrading Cashier
@@ -55,20 +55,20 @@ When upgrading to a new version of Cashier, it's important that you carefully re
## Installation
-First, require the Cashier package for Paddle with Composer:
+First, install the Cashier package for Paddle using the Composer package manager:
composer require laravel/cashier-paddle
> {note} To ensure Cashier properly handles all Paddle events, remember to [set up Cashier's webhook handling](#handling-paddle-webhooks).
-#### Database Migrations
+### Database Migrations
-The Cashier service provider registers its own database migration directory, so remember to migrate your database after installing the package. The Cashier migrations will create a new `customers` table. In addition, a new `subscriptions` table will be created to store all of your customer's subscriptions. Finally, a new `receipts` table will be created to store all of your receipt information:
+The Cashier service provider registers its own database migration directory, so remember to migrate your database after installing the package. The Cashier migrations will create a new `customers` table. In addition, a new `subscriptions` table will be created to store all of your customer's subscriptions. Finally, a new `receipts` table will be created to store all of your application's receipt information:
php artisan migrate
-If you need to overwrite the migrations that ship with the Cashier package, you can publish them using the `vendor:publish` Artisan command:
+If you need to overwrite the migrations that are included with Cashier, you can publish them using the `vendor:publish` Artisan command:
php artisan vendor:publish --tag="cashier-migrations"
@@ -76,7 +76,15 @@ If you would like to prevent Cashier's migrations from running entirely, you may
use Laravel\Paddle\Cashier;
- Cashier::ignoreMigrations();
+ /**
+ * Register any application services.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ Cashier::ignoreMigrations();
+ }
## Configuration
@@ -95,6 +103,7 @@ Before using Cashier, you must add the `Billable` trait to your user model defin
If you have billable entities that are not users, you may also add the trait to those classes:
+ use Illuminate\Database\Eloquent\Model;
use Laravel\Paddle\Billable;
class Team extends Model
@@ -105,7 +114,7 @@ If you have billable entities that are not users, you may also add the trait to
### API Keys
-Next, you should configure your Paddle keys in your `.env` file. You can retrieve your Paddle API keys from the Paddle control panel:
+Next, you should configure your Paddle keys in your application's `.env` file. You can retrieve your Paddle API keys from the Paddle control panel:
PADDLE_VENDOR_ID=your-paddle-vendor-id
PADDLE_VENDOR_AUTH_CODE=your-paddle-vendor-auth-code
@@ -114,7 +123,7 @@ Next, you should configure your Paddle keys in your `.env` file. You can retriev
### Paddle JS
-Paddle relies on its own JavaScript library to initiate the Paddle checkout widget. You can load the JavaScript library by placing the `@paddleJS` directive right before your application layout's closing `` tag:
+Paddle relies on its own JavaScript library to initiate the Paddle checkout widget. You can load the JavaScript library by placing the `@paddleJS` Blade directive right before your application layout's closing `` tag:
...
@@ -125,7 +134,7 @@ Paddle relies on its own JavaScript library to initiate the Paddle checkout widg
### Currency Configuration
-The default Cashier currency is United States Dollars (USD). You can change the default currency by setting the `CASHIER_CURRENCY` environment variable:
+The default Cashier currency is United States Dollars (USD). You can change the default currency by defining a `CASHIER_CURRENCY` environment variable within your application's `.env` file:
CASHIER_CURRENCY=EUR
@@ -141,42 +150,51 @@ In addition to configuring Cashier's currency, you may also specify a locale to
### Pay Links
-Paddle lacks an extensive CRUD API to perform state changes. Therefore, most interactions with Paddle are done through [its checkout widget](https://developer.paddle.com/guides/how-tos/checkout/paddle-checkout). Before we can display the checkout widget, we will generate a "pay link" using Cashier:
+Paddle lacks an extensive CRUD API to perform subscription state changes. Therefore, most interactions with Paddle are done through its [checkout widget](https://developer.paddle.com/guides/how-tos/checkout/paddle-checkout). Before we can display the checkout widget, we must generate a "pay link" using Cashier. A "pay link" will inform the checkout widget of the billing operation we wish to perform:
- $user = User::find(1);
+ use App\Models\User;
+ use Illuminate\Http\Request;
- $payLink = $user->newSubscription('default', $premium = 34567)
- ->returnTo(route('home'))
- ->create();
+ Route::get('/user/subscribe', function (Request $request) {
+ $payLink = $request->user()->newSubscription('default', $premium = 34567)
+ ->returnTo(route('home'))
+ ->create();
- return view('billing', ['payLink' => $payLink]);
+ return view('billing', ['payLink' => $payLink]);
+ });
-Cashier includes a `paddle-button` Blade component. We may pass the pay link URL to this component as a "prop". When this button is clicked, Paddle's checkout widget will be displayed:
+Cashier includes a `paddle-button` [Blade component](/docs/{{version}}/blade#components). We may pass the pay link URL to this component as a "prop". When this button is clicked, Paddle's checkout widget will be displayed:
-
- Subscribe
-
+```html
+
+ Subscribe
+
+```
By default, this will display a button with the standard Paddle styling. You can remove all Paddle styling by adding the `data-theme="none"` attribute to the component:
-
- Subscribe
-
+```html
+
+ Subscribe
+
+```
-The Paddle checkout widget is asynchronous. Once the user creates or updates a subscription within the widget, Paddle will send our application webhooks so that we may properly update the subscription state in our own database. Therefore, it's important that you properly [set up webhooks](#handling-paddle-webhooks) to accommodate for state changes from Paddle.
+The Paddle checkout widget is asynchronous. Once the user creates or updates a subscription within the widget, Paddle will send your application webhooks so that you may properly update the subscription state in our own database. Therefore, it's important that you properly [set up webhooks](#handling-paddle-webhooks) to accommodate for state changes from Paddle.
-After a subscription state change, the delay for receiving the corresponding webhook is typically minimal but you should account for this in your application by considering that your user's subscription might not be immediately available after completing the checkout.
+For more information on pay links, you may review [the Paddle API documentation on pay link generation](https://developer.paddle.com/api-reference/product-api/pay-links/createpaylink).
-For more information, you may review [the Paddle API documentation on pay link generation](https://developer.paddle.com/api-reference/product-api/pay-links/createpaylink).
+> {note} After a subscription state change, the delay for receiving the corresponding webhook is typically minimal but you should account for this in your application by considering that your user's subscription might not be immediately available after completing the checkout.
### Inline Checkout
-If you don't want to make use of the "overlay" style checkout widget, Paddle also has an option to display the widget inline. While this approach does not allow you to adjust any of the checkout's HTML fields, it allows you to embed the widget within your application.
+If you don't want to make use of Paddle's "overlay" style checkout widget, Paddle also provides the option to display the widget inline. While this approach does not allow you to adjust any of the checkout's HTML fields, it allows you to embed the widget within your application.
To make it easy for you to get started with inline checkout, Cashier includes a `paddle-checkout` Blade component. To get started, you should [generate a pay link](#pay-links) and pass the pay link to the component's `override` attribute:
-
+```html
+
+```
To adjust the height of the inline checkout component, you may pass the `height` attribute to the Blade component:
@@ -194,69 +212,73 @@ Alternatively, you may customize the widget with custom options instead of using
-Please consult Paddle's [guide on Inline Checkout](https://developer.paddle.com/guides/how-tos/checkout/inline-checkout) as well as their [Parameter Reference](https://developer.paddle.com/reference/paddle-js/parameters) for further details on available options.
+Please consult Paddle's [guide on Inline Checkout](https://developer.paddle.com/guides/how-tos/checkout/inline-checkout) as well as their [parameter reference](https://developer.paddle.com/reference/paddle-js/parameters) for further details on the inline checkout's available options.
-> {note} If you would like to also use the `passthrough` option when specifying custom options, you should provide a key / value array since Cashier will automatically handle converting the array to a JSON string. In addition, the `customer_id` passthrough option is reserved for internal Cashier usage.
+> {note} If you would like to also use the `passthrough` option when specifying custom options, you should provide a key / value array as its value. Cashier will automatically handle converting the array to a JSON string. In addition, the `customer_id` passthrough option is reserved for internal Cashier usage.
### User Identification
-In contrast to Stripe, Paddle users are unique across the whole of Paddle, not unique per Paddle account. Because of this, Paddle's API's do not currently provide a method to update a user's details such as their email address. When generating pay links, Paddle identifies users using the `customer_email` parameter. When creating a subscription, Paddle will try to match the user provided email to an existing Paddle user.
+In contrast to Stripe, Paddle users are unique across all of Paddle, not unique per Paddle account. Because of this, Paddle's API's do not currently provide a method to update a user's details such as their email address. When generating pay links, Paddle identifies users using the `customer_email` parameter. When creating a subscription, Paddle will try to match the user provided email to an existing Paddle user.
In light of this behavior, there are some important things to keep in mind when using Cashier and Paddle. First, you should be aware that even though subscriptions in Cashier are tied to the same application user, **they could be tied to different users within Paddle's internal systems**. Secondly, each subscription has its own connected payment method information and could also have different email addresses within Paddle's internal systems (depending on which email was assigned to the user when the subscription was created).
-Therefore, when displaying subscriptions you should always inform the user which email address or payment method information is connected to the subscription on a per-subscription basis. Retrieving this information can be done with the following methods on the `Subscription` model:
+Therefore, when displaying subscriptions you should always inform the user which email address or payment method information is connected to the subscription on a per-subscription basis. Retrieving this information can be done with the following methods provided by the `Laravel\Paddle\Subscription` model:
$subscription = $user->subscription('default');
- $customerEmailAddress = $subscription->paddleEmail();
- $paymentMethod = $subscription->paymentMethod();
- $cardBrand = $subscription->cardBrand();
- $cardLastFour = $subscription->cardLastFour();
- $cardExpirationDate = $subscription->cardExpirationDate();
+ $subscription->paddleEmail();
+ $subscription->paymentMethod();
+ $subscription->cardBrand();
+ $subscription->cardLastFour();
+ $subscription->cardExpirationDate();
There is currently no way to modify a user's email address through the Paddle API. When a user wants to update their email address within Paddle, the only way for them to do so is to contact Paddle customer support. When communicating with Paddle, they need to provide the `paddleEmail` value of the subscription to assist Paddle in updating the correct user.
## Prices
-Paddle allows you to customize prices per currency, essentially allowing you to configure different prices for different countries. Cashier Paddle allows you to retrieve all of the prices for a given product using the `productPrices` method:
+Paddle allows you to customize prices per currency, essentially allowing you to configure different prices for different countries. Cashier Paddle allows you to retrieve all of the prices for a given product using the `productPrices` method. This method accepts the product IDs of the products you wish to retrieve prices for:
use Laravel\Paddle\Cashier;
- // Retrieve prices for two products...
$prices = Cashier::productPrices([123, 456]);
The currency will be determined based on the IP address of the request; however, you may optionally provide a specific country to retrieve prices for:
use Laravel\Paddle\Cashier;
- // Retrieve prices for two products...
$prices = Cashier::productPrices([123, 456], ['customer_country' => 'BE']);
After retrieving the prices you may display them however you wish:
-
+```
For more information, [check Paddle's API documentation on prices](https://developer.paddle.com/api-reference/checkout-api/prices/getprices).
@@ -267,7 +289,6 @@ If a user is already a customer and you would like to display the prices that ap
use App\Models\User;
- // Retrieve prices for two products...
$prices = User::find(1)->productPrices([123, 456]);
Internally, Cashier will use the user's [`paddleCountry` method](#customer-defaults) to retrieve the prices in their currency. So, for example, a user living in the United States will see prices in USD while a user in Belgium will see prices in EUR. If no matching currency can be found the default currency of the product will be used. You can customize all prices of a product or subscription plan in the Paddle control panel.
@@ -279,23 +300,29 @@ You may also choose to display prices after a coupon reduction. When calling the
use Laravel\Paddle\Cashier;
- $prices = Cashier::productPrices([123, 456], ['coupons' => 'SUMMERSALE,20PERCENTOFF']);
+ $prices = Cashier::productPrices([123, 456], [
+ 'coupons' => 'SUMMERSALE,20PERCENTOFF'
+ ]);
Then, display the calculated prices using the `price` method:
-
+```
> {note} When using the prices API, Paddle only allows to apply coupons to one-time purchase products and not to subscription plans.
@@ -305,7 +332,7 @@ You may display the original listed prices (without coupon discounts) using the
### Customer Defaults
-Cashier allows you to set some useful defaults for your customer when creating pay links. Setting these defaults allow you to pre-fill a customer's email address, country, and postcode so that they can immediately move on to the payment portion of the checkout widget. You can set these defaults by overriding the following methods on your billable user:
+Cashier allows you to define some useful defaults for your customers when creating pay links. Setting these defaults allow you to pre-fill a customer's email address, country, and postal code so that they can immediately move on to the payment portion of the checkout widget. You can set these defaults by overriding the following methods on your billable model:
/**
* Get the customer's email address to associate with Paddle.
@@ -331,7 +358,7 @@ Cashier allows you to set some useful defaults for your customer when creating p
}
/**
- * Get the customer's postcode to associate with Paddle.
+ * Get the customer's postal code to associate with Paddle.
*
* See the link below for countries which require this.
*
@@ -353,28 +380,32 @@ These defaults will be used for every action in Cashier that generates a [pay li
To create a subscription, first retrieve an instance of your billable model, which typically will be an instance of `App\Models\User`. Once you have retrieved the model instance, you may use the `newSubscription` method to create the model's subscription pay link:
- $user = User::find(1);
+ use Illuminate\Http\Request;
- $payLink = $user->newSubscription('default', $premium = 12345)
- ->returnTo(route('home'))
- ->create();
+ Route::get('/user/subscribe', function (Request $request) {
+ $payLink = $user->newSubscription('default', $premium = 12345)
+ ->returnTo(route('home'))
+ ->create();
- return view('billing', ['payLink' => $payLink]);
+ return view('billing', ['payLink' => $payLink]);
+ });
The first argument passed to the `newSubscription` method should be the name of the subscription. If your application only offers a single subscription, you might call this `default` or `primary`. The second argument is the specific plan the user is subscribing to. This value should correspond to the plan's identifier in Paddle. The `returnTo` method accepts a URL that your user will be redirected to after they successfully complete the checkout.
-The `create` method will create a pay link which you can use to generate a payment button. The payment button can be generated using the `paddle-button` Blade component that ships with Cashier Paddle:
+The `create` method will create a pay link which you can use to generate a payment button. The payment button can be generated using the `paddle-button` [Blade component](/docs/{{version}}/blade#components) that is included with Cashier Paddle:
-
- Subscribe
-
+```html
+
+ Subscribe
+
+```
After the user has finished their checkout, a `subscription_created` webhook will be dispatched from Paddle. Cashier will receive this webhook and setup the subscription for your customer. In order to make sure all webhooks are properly received and handled by your application, ensure you have properly [setup webhook handling](#handling-paddle-webhooks).
#### Additional Details
-If you would like to specify additional customer or subscription details, you may do so by passing them as a key / value array to the `create` method:
+If you would like to specify additional customer or subscription details, you may do so by passing them as an array of key / value pairs to the `create` method. To learn more about the additional fields supported by Paddle, check out Paddle's documentation on [generating pay links](https://developer.paddle.com/api-reference/product-api/pay-links/createpaylink):
$payLink = $user->newSubscription('default', $monthly = 12345)
->returnTo(route('home'))
@@ -382,8 +413,6 @@ If you would like to specify additional customer or subscription details, you ma
'vat_number' => $vatNumber,
]);
-To learn more about the additional fields supported by Paddle, check out Paddle's documentation on [generating pay links](https://developer.paddle.com/api-reference/product-api/pay-links/createpaylink).
-
#### Coupons
@@ -417,17 +446,33 @@ Once a user is subscribed to your application, you may check their subscription
The `subscribed` method also makes a great candidate for a [route middleware](/docs/{{version}}/middleware), allowing you to filter access to routes and controllers based on the user's subscription status:
- public function handle($request, Closure $next)
+ user() && ! $request->user()->subscribed('default')) {
- // This user is not a paying customer...
- return redirect('billing');
- }
+ /**
+ * Handle an incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @return mixed
+ */
+ public function handle($request, Closure $next)
+ {
+ if ($request->user() && ! $request->user()->subscribed('default')) {
+ // This user is not a paying customer...
+ return redirect('billing');
+ }
- return $next($request);
+ return $next($request)
+ }
}
-If you would like to determine if a user is still within their trial period, you may use the `onTrial` method. This method can be useful for displaying a warning to the user that they are still on their trial period:
+If you would like to determine if a user is still within their trial period, you may use the `onTrial` method. This method can be useful for determining if you should display a warning to the user that they are still on their trial period:
if ($user->subscription('default')->onTrial()) {
//
@@ -454,7 +499,7 @@ The `recurring` method may be used to determine if the user is currently subscri
#### Cancelled Subscription Status
-To determine if the user was once an active subscriber, but has cancelled their subscription, you may use the `cancelled` method:
+To determine if the user was once an active subscriber but has cancelled their subscription, you may use the `cancelled` method:
if ($user->subscription('default')->cancelled()) {
//
@@ -472,6 +517,33 @@ To determine if the user has cancelled their subscription and is no longer withi
//
}
+
+#### Past Due Status
+
+If a payment fails for a subscription, it will be marked as `past_due`. When your subscription is in this state it will not be active until the customer has updated their payment information. You may determine if a subscription is past due using the `pastDue` method on the subscription instance:
+
+ if ($user->subscription('default')->pastDue()) {
+ //
+ }
+
+When a subscription is past due, you should instruct the user to [update their payment information](#updating-payment-information). You may configure how past due subscriptions are handled in your [Paddle subscription settings](https://vendors.paddle.com/subscription-settings).
+
+If you would like subscriptions to still be considered active when they are `past_due`, you may use the `keepPastDueSubscriptionsActive` method provided by Cashier. Typically, this method should be called in the `register` method of your `AppServiceProvider`:
+
+ use Laravel\Paddle\Cashier;
+
+ /**
+ * Register any application services.
+ *
+ * @return void
+ */
+ public function register()
+ {
+ Cashier::keepPastDueSubscriptionsActive();
+ }
+
+> {note} When a subscription is in a `past_due` state it cannot be changed until payment information has been updated. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in a `past_due` state.
+
#### Subscription Scopes
@@ -500,33 +572,6 @@ A complete list of available scopes is available below:
Subscription::query()->onGracePeriod();
Subscription::query()->notOnGracePeriod();
-
-#### Past Due Status
-
-If a payment fails for a subscription, it will be marked as `past_due`. When your subscription is in this state it will not be active until the customer has updated their payment information. You may determine if a subscription is past due using the `pastDue` method on the subscription instance:
-
- if ($user->subscription('default')->pastDue()) {
- //
- }
-
-When a subscription is past due, you should instruct the user to [update their payment information](#updating-payment-information). You may configure how past due subscriptions are handled in your [Paddle subscription settings](https://vendors.paddle.com/subscription-settings).
-
-If you would like subscriptions to still be considered active when they are `past_due`, you may use the `keepPastDueSubscriptionsActive` method provided by Cashier. Typically, this method should be called in the `register` method of your `AppServiceProvider`:
-
- use Laravel\Paddle\Cashier;
-
- /**
- * Register any application services.
- *
- * @return void
- */
- public function register()
- {
- Cashier::keepPastDueSubscriptionsActive();
- }
-
-> {note} When a subscription is in a `past_due` state it cannot be changed until payment information has been updated. Therefore, the `swap` and `updateQuantity` methods will throw an exception when the subscription is in a `past_due` state.
-
### Subscription Single Charges
@@ -534,35 +579,41 @@ Subscription single charges allow you to charge subscribers with a one-time char
$response = $user->subscription('default')->charge(12.99, 'Support Add-on');
-In contrast to [single charges](#single-charges), this method will immediately charge the customer's stored payment method for the subscription. The charge amount is always in the currency of which the subscription currently is set to.
+In contrast to [single charges](#single-charges), this method will immediately charge the customer's stored payment method for the subscription. The charge amount should always be defined in the currency of the subscription.
### Updating Payment Information
Paddle always saves a payment method per subscription. If you want to update the default payment method for a subscription, you should first generate a subscription "update URL" using the `updateUrl` method on the subscription model:
- $user = App\Models\User::find(1);
+ use App\Models\User;
+
+ $user = User::find(1);
$updateUrl = $user->subscription('default')->updateUrl();
Then, you may use the generated URL in combination with Cashier's provided `paddle-button` Blade component to allow the user to initiate the Paddle widget and update their payment information:
-
- Update Card
-
+```html
+
+ Update Card
+
+```
When a user has finished updating their information, a `subscription_updated` webhook will be dispatched by Paddle and the subscription details will be updated in your application's database.
### Changing Plans
-After a user has subscribed to your application, they may occasionally want to change to a new subscription plan. To swap a user to a new subscription, you should pass the Paddle plan's identifier to the subscription's `swap` method:
+After a user has subscribed to your application, they may occasionally want to change to a new subscription plan. To update the subscription plan for a user, you should pass the Paddle plan's identifier to the subscription's `swap` method:
- $user = App\Models\User::find(1);
+ use App\Models\User;
+
+ $user = User::find(1);
$user->subscription('default')->swap($premium = 34567);
-If the user is on trial, the trial period will be maintained. Also, if a "quantity" exists for the subscription, that quantity will also be maintained.
+If the user is on a trial, the trial period will be maintained. Also, if a "quantity" exists for the subscription, that quantity will also be maintained.
If you would like to swap plans and cancel any trial period the user is currently on, you may use the `skipTrial` method:
@@ -572,7 +623,7 @@ If you would like to swap plans and cancel any trial period the user is currentl
If you would like to swap plans and immediately invoice the user instead of waiting for their next billing cycle, you may use the `swapAndInvoice` method:
- $user = App\Models\User::find(1);
+ $user = User::find(1);
$user->subscription('default')->swapAndInvoice($premium = 34567);
@@ -586,7 +637,7 @@ By default, Paddle prorates charges when swapping between plans. The `noProrate`
### Subscription Quantity
-Sometimes subscriptions are affected by "quantity". For example, your application might charge $10 per month **per user** on an account. To easily increment or decrement your subscription quantity, use the `incrementQuantity` and `decrementQuantity` methods:
+Sometimes subscriptions are affected by "quantity". For example, a project management application might charge $10 per month per project. To easily increment or decrement your subscription's quantity, use the `incrementQuantity` and `decrementQuantity` methods:
$user = User::find(1);
@@ -597,7 +648,7 @@ Sometimes subscriptions are affected by "quantity". For example, your applicatio
$user->subscription('default')->decrementQuantity();
- // Subtract five to the subscription's current quantity...
+ // Subtract five from the subscription's current quantity...
$user->subscription('default')->decrementQuantity(5);
Alternatively, you may set a specific quantity using the `updateQuantity` method:
@@ -615,7 +666,7 @@ To pause a subscription, call the `pause` method on the user's subscription:
$user->subscription('default')->pause();
-When a subscription is paused, Cashier will automatically set the `paused_from` column in your database. This column is used to know when the `paused` method should begin returning `true`. For example, if a customer pauses a subscription on March 1st, but the subscription was not scheduled to recur until March 5th, the `paused` method will continue to return `false` until March 5th.
+When a subscription is paused, Cashier will automatically set the `paused_from` column in your database. This column is used to know when the `paused` method should begin returning `true`. For example, if a customer pauses a subscription on March 1st, but the subscription was not scheduled to recur until March 5th, the `paused` method will continue to return `false` until March 5th. This is done because a user is typically allowed to continue using an application until the end of their billing cycle.
You may determine if a user has paused their subscription but are still on their "grace period" using the `onPausedGracePeriod` method:
@@ -636,7 +687,7 @@ To cancel a subscription, call the `cancel` method on the user's subscription:
$user->subscription('default')->cancel();
-When a subscription is cancelled, Cashier will automatically set the `ends_at` column in your database. This column is used to know when the `subscribed` method should begin returning `false`. For example, if a customer cancels a subscription on March 1st, but the subscription was not scheduled to end until March 5th, the `subscribed` method will continue to return `true` until March 5th.
+When a subscription is cancelled, Cashier will automatically set the `ends_at` column in your database. This column is used to know when the `subscribed` method should begin returning `false`. For example, if a customer cancels a subscription on March 1st, but the subscription was not scheduled to end until March 5th, the `subscribed` method will continue to return `true` until March 5th. This is done because a user is typically allowed to continue using an application until the end of their billing cycle.
You may determine if a user has cancelled their subscription but are still on their "grace period" using the `onGracePeriod` method:
@@ -660,20 +711,22 @@ If you wish to cancel a subscription immediately, you may call the `cancelNow` m
If you would like to offer trial periods to your customers while still collecting payment method information up front, you should use the `trialDays` method when creating your subscription pay links:
- $user = User::find(1);
+ use Illuminate\Http\Request;
- $payLink = $user->newSubscription('default', $monthly = 12345)
- ->returnTo(route('home'))
- ->trialDays(10)
- ->create();
+ Route::get('/user/subscribe', function (Request $request) {
+ $payLink = $request->user()->newSubscription('default', $monthly = 12345)
+ ->returnTo(route('home'))
+ ->trialDays(10)
+ ->create();
- return view('billing', ['payLink' => $payLink]);
+ return view('billing', ['payLink' => $payLink]);
+ });
-This method will set the trial period ending date on the subscription record within the database, as well as instruct Paddle to not begin billing the customer until after this date.
+This method will set the trial period ending date on the subscription record within your application's database, as well as instruct Paddle to not begin billing the customer until after this date.
> {note} If the customer's subscription is not cancelled before the trial ending date they will be charged as soon as the trial expires, so you should be sure to notify your users of their trial ending date.
-You may determine if the user is within their trial period using either the `onTrial` method of the user instance or the `onTrial` method of the subscription instance. The two examples below have identical behavior:
+You may determine if the user is within their trial period using either the `onTrial` method of the user instance or the `onTrial` method of the subscription instance. The two examples below are equivalent:
if ($user->onTrial('default')) {
//
@@ -693,8 +746,10 @@ You may choose to define how many trial days your plan's receive in the Paddle d
If you would like to offer trial periods without collecting the user's payment method information up front, you may set the `trial_ends_at` column on the customer record attached to your user to your desired trial ending date. This is typically done during user registration:
+ use App\Models\User;
+
$user = User::create([
- // Other user properties...
+ // ...
]);
$user->createAsCustomer([
@@ -709,11 +764,15 @@ Cashier refers to this type of trial as a "generic trial", since it is not attac
Once you are ready to create an actual subscription for the user, you may use the `newSubscription` method as usual:
- $user = User::find(1);
+ use Illuminate\Http\Request;
- $payLink = $user->newSubscription('default', $monthly = 12345)
- ->returnTo(route('home'))
- ->create();
+ Route::get('/user/subscribe', function (Request $request) {
+ $payLink = $user->newSubscription('default', $monthly = 12345)
+ ->returnTo(route('home'))
+ ->create();
+
+ return view('billing', ['payLink' => $payLink]);
+ });
To retrieve the user's trial ending date, you may use the `trialEndsAt` method. This method will return a Carbon date instance if a user is on a trial or `null` if they aren't. You may also pass an optional subscription name parameter if you would like to get the trial ending date for a specific subscription other than the default one:
@@ -721,7 +780,7 @@ To retrieve the user's trial ending date, you may use the `trialEndsAt` method.
$trialEndsAt = $user->trialEndsAt('main');
}
-You may also use the `onGenericTrial` method if you wish to know specifically that the user is within their "generic" trial period and has not created an actual subscription yet:
+You may use the `onGenericTrial` method if you wish to know specifically that the user is within their "generic" trial period and has not created an actual subscription yet:
if ($user->onGenericTrial()) {
// User is within their "generic" trial period...
@@ -732,13 +791,11 @@ You may also use the `onGenericTrial` method if you wish to know specifically th
## Handling Paddle Webhooks
-> {tip} You may use [Valet's `share` command](https://laravel.com/docs/{{version}}/valet#sharing-sites) to help test webhooks during local development.
+Paddle can notify your application of a variety of events via webhooks. By default, a route that points to Cashier's webhook controller is registered by the Cashier service provider. This controller will handle all incoming webhook requests.
-Paddle can notify your application of a variety of events via webhooks. By default, a route that points to Cashier's webhook controller is configured through the Cashier service provider. This controller will handle all incoming webhook requests.
+By default, this controller will automatically handle cancelling subscriptions that have too many failed charges ([as defined by your Paddle subscription settings](https://vendors.paddle.com/subscription-settings)), subscription updates, and payment method changes; however, as we'll soon discover, you can extend this controller to handle any Paddle webhook event you like.
-By default, this controller will automatically handle cancelling subscriptions that have too many failed charges ([as defined by your Paddle subscription settings](https://vendors.paddle.com/subscription-settings)), subscription updates, and payment method changes; however, as we'll soon discover, you can extend this controller to handle any webhook event you like.
-
-To ensure your application can handle Paddle webhooks, be sure to [configure the webhook URL in the Paddle control panel](https://vendors.paddle.com/alerts-webhooks). By default, Cashier's webhook controller listens to the `/paddle/webhook` URL path. The full list of all webhooks you should configure in the Paddle control panel are:
+To ensure your application can handle Paddle webhooks, be sure to [configure the webhook URL in the Paddle control panel](https://vendors.paddle.com/alerts-webhooks). By default, Cashier's webhook controller responds to the `/paddle/webhook` URL path. The full list of all webhooks you should enable in the Paddle control panel are:
- Subscription Created
- Subscription Updated
@@ -751,7 +808,7 @@ To ensure your application can handle Paddle webhooks, be sure to [configure the
#### Webhooks & CSRF Protection
-Since Paddle webhooks need to bypass Laravel's [CSRF protection](/docs/{{version}}/csrf), be sure to list the URI as an exception in your `VerifyCsrfToken` middleware or list the route outside of the `web` middleware group:
+Since Paddle webhooks need to bypass Laravel's [CSRF protection](/docs/{{version}}/csrf), be sure to list the URI as an exception in your `App\Http\Middleware\VerifyCsrfToken` middleware or list the route outside of the `web` middleware group:
protected $except = [
'paddle/*',
@@ -760,7 +817,9 @@ Since Paddle webhooks need to bypass Laravel's [CSRF protection](/docs/{{version
### Defining Webhook Event Handlers
-Cashier automatically handles subscription cancellation on failed charges, but if you have additional webhook events you would like to handle, you should extend the `WebhookController`. Your method names should correspond to Cashier's expected convention, specifically, methods should be prefixed with `handle` and the "camel case" name of the webhook you wish to handle. For example, if you wish to handle the `payment_succeeded` webhook, you should add a `handlePaymentSucceeded` method to the controller:
+Cashier automatically handles subscription cancellation on failed charges and other common Paddle webhooks, but if you have additional webhook events you would like to handle, you should extend Cashier's `WebhookController`.
+
+Your controller's method names should correspond to Cashier's controller method conventions. Specifically, methods should be prefixed with `handle` and the "camel case" name of the webhook you wish to handle. For example, if you wish to handle the `payment_succeeded` webhook, you should add a `handlePaymentSucceeded` method to the controller:
-- `PaymentSucceeded`
-- `SubscriptionPaymentSucceeded`
-- `SubscriptionCreated`
-- `SubscriptionUpdated`
-- `SubscriptionCancelled`
+- `Laravel\Paddle\Events\PaymentSucceeded`
+- `Laravel\Paddle\Events\SubscriptionPaymentSucceeded`
+- `Laravel\Paddle\Events\SubscriptionCreated`
+- `Laravel\Paddle\Events\SubscriptionUpdated`
+- `Laravel\Paddle\Events\SubscriptionCancelled`
-You can optionally also override the default, built-in webhook route by setting the `CASHIER_WEBHOOK` env variable in your `.env` file. This value should be the full URL to your webhook route and needs to match the URL set in your Paddle control panel:
+You can also override the default, built-in webhook route by defining the `CASHIER_WEBHOOK` environment variable in your application's `.env` file. This value should be the full URL to your webhook route and needs to match the URL set in your Paddle control panel:
- CASHIER_WEBHOOK=https://example.com/my-paddle-webhook-url
-
-
-### Failed Subscriptions
-
-What if a customer's credit card expires? No worries - Cashier's Webhook controller will cancel the customer's subscription for you. Failed payments will automatically be captured and handled by the controller. The controller will cancel the customer's subscription when Paddle determines the subscription has failed (normally after three failed payment attempts).
+```bash
+CASHIER_WEBHOOK=https://example.com/my-paddle-webhook-url
+```
### Verifying Webhook Signatures
To secure your webhooks, you may use [Paddle's webhook signatures](https://developer.paddle.com/webhook-reference/verifying-webhooks). For convenience, Cashier automatically includes a middleware which validates that the incoming Paddle webhook request is valid.
-To enable webhook verification, ensure that the `PADDLE_PUBLIC_KEY` environment variable is set in your `.env` file. The public key may be retrieved from your Paddle account dashboard.
+To enable webhook verification, ensure that the `PADDLE_PUBLIC_KEY` environment variable is defined in your application's `.env` file. The public key may be retrieved from your Paddle account dashboard.
## Single Charges
@@ -822,49 +878,63 @@ To enable webhook verification, ensure that the `PADDLE_PUBLIC_KEY` environment
### Simple Charge
-If you would like to make a "one off" charge against a customer, you may use the `charge` method on a billable model instance to generate a pay link for the charge. The `charge` method accepts the charge amount (float) as its first argument and a charge description as its second argument:
+If you would like to make a one-time charge against a customer, you may use the `charge` method on a billable model instance to generate a pay link for the charge. The `charge` method accepts the charge amount (float) as its first argument and a charge description as its second argument:
- $payLink = $user->charge(12.99, 'Product Title');
+ use Illuminate\Http\Request;
- return view('pay', ['payLink' => $payLink]);
+ Route::get('/store', function (Request $request) {
+ return view('store', [
+ 'payLink' => $user->charge(12.99, 'Action Figure')
+ ]);
+ });
After generating the pay link, you may use Cashier's provided `paddle-button` Blade component to allow the user to initiate the Paddle widget and complete the charge:
-
- Buy
-
+```html
+
+ Buy
+
+```
The `charge` method accepts an array as its third argument, allowing you to pass any options you wish to the underlying Paddle pay link creation. Please consult [the Paddle documentation](https://developer.paddle.com/api-reference/product-api/pay-links/createpaylink) to learn more about the options available to you when creating charges:
- $payLink = $user->charge(12.99, 'Product Title', [
+ $payLink = $user->charge(12.99, 'Action Figure', [
'custom_option' => $value,
]);
-Charges happen in the currency specified in the `cashier.currency` configuration option. By default, this is set to USD. You may override the default currency by setting the `CASHIER_CURRENCY` in your `.env` file:
+Charges happen in the currency specified in the `cashier.currency` configuration option. By default, this is set to USD. You may override the default currency by defining the `CASHIER_CURRENCY` environment variable in your application's `.env` file:
- CASHIER_CURRENCY=EUR
+```bash
+CASHIER_CURRENCY=EUR
+```
You can also [override prices per currency](https://developer.paddle.com/api-reference/product-api/pay-links/createpaylink#price-overrides) using Paddle's dynamic pricing matching system. To do so, pass an array of prices instead of a fixed amount:
$payLink = $user->charge([
'USD:19.99',
'EUR:15.99',
- ], 'Product Title');
+ ], 'Action Figure');
### Charging Products
-If you would like to make a "one off" charge against a specific product configured within Paddle, you may use the `chargeProduct` method on a billable model instance to generate a pay link:
+If you would like to make a one-time charge against a specific product configured within Paddle, you may use the `chargeProduct` method on a billable model instance to generate a pay link:
- $payLink = $user->chargeProduct($productId);
+ use Illuminate\Http\Request;
- return view('pay', ['payLink' => $payLink]);
+ Route::get('/store', function (Request $request) {
+ return view('store', [
+ 'payLink' => $request->user()->chargeProduct($productId = 123)
+ ]);
+ });
Then, you may provide the pay link to the `paddle-button` component to allow the user to initialize the Paddle widget:
-
- Buy
-
+```html
+
+ Buy
+
+```
The `chargeProduct` method accepts an array as its second argument, allowing you to pass any options you wish to the underlying Paddle pay link creation. Please consult [the Paddle documentation](https://developer.paddle.com/api-reference/product-api/pay-links/createpaylink) regarding the options that are available to you when creating charges:
@@ -875,13 +945,17 @@ The `chargeProduct` method accepts an array as its second argument, allowing you
### Refunding Orders
-If you need to refund a Paddle order, you may use the `refund` method. This method accepts the Paddle Order ID as its first argument. You may retrieve receipts for a given billable entity using the `receipts` method:
+If you need to refund a Paddle order, you may use the `refund` method. This method accepts the Paddle order ID as its first argument. You may retrieve the receipts for a given billable model using the `receipts` method:
+
+ use App\Models\User;
+
+ $user = User::find(1);
$receipt = $user->receipts()->first();
$refundRequestId = $user->refund($receipt->order_id);
-You may also optionally specify a specific amount to refund as well as a reason for the refund:
+You may optionally specify a specific amount to refund as well as a reason for the refund:
$receipt = $user->receipts()->first();
@@ -896,24 +970,34 @@ You may also optionally specify a specific amount to refund as well as a reason
You may easily retrieve an array of a billable model's receipts using the `receipts` method:
+ use App\Models\User;
+
+ $user = User::find(1);
+
$receipts = $user->receipts();
-When listing the receipts for the customer, you may use the receipt's helper methods to display the relevant receipt information. For example, you may wish to list every receipt in a table, allowing the user to easily download any of the receipts:
+When listing the receipts for the customer, you may use the receipt instance's methods to display the relevant receipt information. For example, you may wish to list every receipt in a table, allowing the user to easily download any of the receipts:
-
+```
### Past & Upcoming Payments
-You may use the `lastPayment` and `nextPayment` methods to display a customer's past or upcoming payments for recurring subscriptions:
+You may use the `lastPayment` and `nextPayment` methods to retrieve and display a customer's past or upcoming payments for recurring subscriptions:
+
+ use App\Models\User;
+
+ $user = User::find(1);
$subscription = $user->subscription('default');
diff --git a/collections.md b/collections.md
index 5a44ecab4f3..a6c8b16b9fd 100644
--- a/collections.md
+++ b/collections.md
@@ -18,8 +18,7 @@ The `Illuminate\Support\Collection` class provides a fluent, convenient wrapper
$collection = collect(['taylor', 'abigail', null])->map(function ($name) {
return strtoupper($name);
- })
- ->reject(function ($name) {
+ })->reject(function ($name) {
return empty($name);
});
@@ -37,7 +36,7 @@ As mentioned above, the `collect` helper returns a new `Illuminate\Support\Colle
### Extending Collections
-Collections are "macroable", which allows you to add additional methods to the `Collection` class at run time. For example, the following code adds a `toUpper` method to the `Collection` class:
+Collections are "macroable", which allows you to add additional methods to the `Collection` class at run time. The `Illuminate\Support\Collection` class' `macro` method accepts a closure that will be executed when your macro is called. The macro closure may access the collection's other methods via `$this`, just as if it were a real method of the collection class. For example, the following code adds a `toUpper` method to the `Collection` class:
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
@@ -54,12 +53,31 @@ Collections are "macroable", which allows you to add additional methods to the `
// ['FIRST', 'SECOND']
-Typically, you should declare collection macros in a [service provider](/docs/{{version}}/providers).
+Typically, you should declare collection macros in the `boot` method of a [service provider](/docs/{{version}}/providers).
+
+
+#### Macro Arguments
+
+If necessary, you may define macros that accept additional arguments:
+
+ use Illuminate\Support\Collection;
+ use Illuminate\Support\Facades\Lang;
+ use Illuminate\Support\Str;
+
+ Collection::macro('toLocale', function ($locale) {
+ return $this->map(function ($value) use ($locale) {
+ return Lang::get($value, $locale);
+ });
+ });
+
+ $collection = collect(['first', 'second']);
+
+ $translated = $collection->toLocale('es');
## Available Methods
-For the remainder of this documentation, we'll discuss each method available on the `Collection` class. Remember, all of these methods may be chained to fluently manipulate the underlying array. Furthermore, almost every method returns a new `Collection` instance, allowing you to preserve the original copy of the collection when necessary:
+For the majority of the remaining collection documentation, we'll discuss each method available on the `Collection` class. Remember, all of these methods may be chained to fluently manipulate the underlying array. Furthermore, almost every method returns a new `Collection` instance, allowing you to preserve the original copy of the collection when necessary:
+
+
+
+
+#### `bigIncrements()` {#collection-method .first-collection-method}
+
+The `bigIncrements` method creates an auto-incrementing `UNSIGNED BIGINT` (primary key) equivalent column:
+
+ $table->bigIncrements('id');
+
+
+#### `bigInteger()` {#collection-method}
+
+The `bigInteger` method creates a `BIGINT` equivalent column:
+
+ $table->bigInteger('votes');
+
+
+#### `binary()` {#collection-method}
+
+The `binary` method creates a `BLOB` equivalent column:
+
+ $table->binary('photo');
+
+
+#### `boolean()` {#collection-method}
+
+The `boolean` method creates a `BOOLEAN` equivalent column:
+
+ $table->boolean('confirmed');
+
+
+#### `char()` {#collection-method}
+
+The `char` method creates a `CHAR` equivalent column with of a given length:
+
+ $table->char('name', 100);
+
+
+#### `dateTimeTz()` {#collection-method}
+
+The `dateTimeTz` method creates a `DATETIME` (with timezone) equivalent column with an optional precision (total digits):
+
+ $table->dateTimeTz('created_at', $precision = 0);
+
+
+#### `dateTime()` {#collection-method}
+
+The `dateTime` method creates a `DATETIME` equivalent column with an optional precision (total digits):
+
+ $table->dateTime('created_at', $precision = 0);
+
+
+#### `date()` {#collection-method}
+
+The `date` method creates a `DATE` equivalent column:
+
+ $table->date('created_at');
+
+
+#### `decimal()` {#collection-method}
+
+The `decimal` method creates a `DECIMAL` equivalent column with the given precision (total digits) and scale (decimal digits):
+
+ $table->decimal('amount', $precision = 8, $scale = 2);
+
+
+#### `double()` {#collection-method}
+
+The `double` method creates a `DOUBLE` equivalent column with the given precision (total digits) and scale (decimal digits):
+
+ $table->double('amount', 8, 2);
+
+
+#### `enum()` {#collection-method}
+
+The `enum` method creates a `ENUM` equivalent column with the given valid values:
+
+ $table->enum('difficulty', ['easy', 'hard']);
+
+
+#### `float()` {#collection-method}
+
+The `float` method creates a `FLOAT` equivalent column with the given precision (total digits) and scale (decimal digits):
+
+ $table->float('amount', 8, 2);
+
+
+#### `foreignId()` {#collection-method}
+
+The `foreignId` method is an alias of the `unsignedBigInteger` method:
+
+ $table->foreignId('user_id');
+
+
+#### `geometryCollection()` {#collection-method}
+
+The `geometryCollection` method creates a `GEOMETRYCOLLECTION` equivalent column:
+
+ $table->geometryCollection('positions');
+
+
+#### `geometry()` {#collection-method}
+
+The `geometry` method creates a `GEOMETRY` equivalent column:
+
+ $table->geometry('positions');
+
+
+#### `id()` {#collection-method}
+
+The `id` method is an alias of the `bigIncrements` method. By default, the method will create an `id` column; however, you may pass a column name if you would like to assign a different name to the column:
+
+ $table->id();
+
+
+#### `increments()` {#collection-method}
+
+The `increments` method creates an auto-incrementing `UNSIGNED INTEGER` equivalent column as a primary key:
+
+ $table->increments('id');
+
+
+#### `integer()` {#collection-method}
+
+The `integer` method creates an `INTEGER` equivalent column:
+
+ $table->integer('votes');
+
+
+#### `ipAddress()` {#collection-method}
+
+The `ipAddress` method creates an `INTEGER` equivalent column:
+
+ $table->ipAddress('visitor');
+
+
+#### `json()` {#collection-method}
+
+The `json` method creates an `JSON` equivalent column:
+
+ $table->json('options');
+
+
+#### `jsonb()` {#collection-method}
+
+The `jsonb` method creates an `JSONB` equivalent column:
+
+ $table->jsonb('options');
+
+
+#### `lineString()` {#collection-method}
+
+The `lineString` method creates an `LINESTRING` equivalent column:
+
+ $table->lineString('positions');
+
+
+#### `longText()` {#collection-method}
+
+The `longText` method creates an `LONGTEXT` equivalent column:
+
+ $table->longText('description');
+
+
+#### `macAddress()` {#collection-method}
+
+The `macAddress` method creates a column that is intended to hold a MAC address. Some database systems, such as PostgreSQL, have a dedicated column type for this type of data. Other database systems will use a string equivalent column:
+
+ $table->macAddress('device');
+
+
+#### `mediumIncrements()` {#collection-method}
+
+The `mediumIncrements` method creates an auto-incrementing `UNSIGNED MEDIUMINT` equivalent column as a primary key:
+
+ $table->mediumIncrements('id');
+
+
+#### `mediumInteger()` {#collection-method}
+
+The `mediumInteger` method creates an `MEDIUMINT` equivalent column:
+
+ $table->mediumInteger('votes');
+
+
+#### `mediumText()` {#collection-method}
+
+The `mediumText` method creates an `MEDIUMTEXT` equivalent column:
+
+ $table->mediumText('description');
+
+
+#### `morphs()` {#collection-method}
+
+The `morphs` method is a convenience method that adds a `{column}_id` `UNSIGNED BIGINT` equivalent column and a `{column}_type` `VARCHAR` equivalent column.
+
+This method is intended to be used when defining the columns necessary for a polymorphic [Eloquent relationship](/docs/{{version}}/eloquent-relationships). In the following example, `taggable_id` and `taggable_type` columns would be created:
+
+ $table->morphs('taggable');
+
+
+#### `multiLineString()` {#collection-method}
+
+The `multiLineString` method creates an `MULTILINESTRING` equivalent column:
+
+ $table->multiLineString('positions');
+
+
+#### `multiPoint()` {#collection-method}
+
+The `multiPoint` method creates an `MULTIPOINT` equivalent column:
+
+ $table->multiPoint('positions');
+
+
+#### `multiPolygon()` {#collection-method}
+
+The `multiPolygon` method creates an `MULTIPOLYGON` equivalent column:
+
+ $table->multiPolygon('positions');
+
+
+#### `nullableTimestamps()` {#collection-method}
+
+The method is similar to the [timestamps](#column-method-timestamps) method; however, the column that is created will be "nullable":
+
+ $table->nullableTimestamps(0);
+
+
+#### `nullableMorphs()` {#collection-method}
+
+The method is similar to the [morphs](#column-method-morphs) method; however, the columns that are created will be "nullable":
+
+ $table->nullableMorphs('taggable');
+
+
+#### `nullableUuidMorphs()` {#collection-method}
+
+The method is similar to the [uuidMorphs](#column-method-uuidMorphs) method; however, the columns that are created will be "nullable":
+
+ $table->nullableUuidMorphs('taggable');
+
+
+#### `point()` {#collection-method}
+
+The `point` method creates an `POINT` equivalent column:
+
+ $table->point('position');
+
+
+#### `polygon()` {#collection-method}
+
+The `polygon` method creates an `POLYGON` equivalent column:
+
+ $table->polygon('position');
+
+
+#### `rememberToken()` {#collection-method}
+
+The `rememberToken` method creates a nullable, `VARCHAR(100)` equivalent column that is intended to store the current "remember me" [authentication token](/docs/{{version}}/authentication#remembering-users):
+
+ $table->rememberToken();
+
+
+#### `set()` {#collection-method}
+
+The `set` method creates an `SET` equivalent column with the given list of valid values:
+
+ $table->set('flavors', ['strawberry', 'vanilla']);
+
+
+#### `smallIncrements()` {#collection-method}
+
+The `smallIncrements` method creates an auto-incrementing `UNSIGNED SMALLINT` equivalent column as a primary key:
+
+ $table->smallIncrements('id');
+
+
+#### `smallInteger()` {#collection-method}
+
+The `smallInteger` method creates an `SMALLINT` equivalent column:
+
+ $table->smallInteger('votes');
+
+
+#### `softDeletesTz()` {#collection-method}
+
+The `softDeletesTz` method adds a nullable `deleted_at` `TIMESTAMP` (with timezone) equivalent column with an optional precision (total digits). This column is intended to store the `deleted_at` timestamp needed for Eloquent's "soft delete" functionality:
+
+ $table->softDeletesTz($column = 'deleted_at', $precision = 0);
+
+
+#### `softDeletes()` {#collection-method}
+
+The `softDeletes` method adds a nullable `deleted_at` `TIMESTAMP` equivalent column with an optional precision (total digits). This column is intended to store the `deleted_at` timestamp needed for Eloquent's "soft delete" functionality:
+
+ $table->softDeletes($column = 'deleted_at', $precision = 0);
+
+
+#### `string()` {#collection-method}
+
+The `string` method creates an `VARCHAR` equivalent column of the given length:
+
+ $table->string('name', 100);
+
+
+#### `text()` {#collection-method}
+
+The `text` method creates an `TEXT` equivalent column:
+
+ $table->text('description');
+
+
+#### `timeTz()` {#collection-method}
+
+The `timeTz` method creates an `TIME` (with timezone) equivalent column with an optional precision (total digits):
+
+ $table->timeTz('sunrise', $precision = 0);
+
+
+#### `time()` {#collection-method}
+
+The `time` method creates an `TIME` equivalent column with an optional precision (total digits):
+
+ $table->time('sunrise', $precision = 0);
+
+
+#### `timestampTz()` {#collection-method}
+
+The `timestampTz` method creates an `TIMESTAMP` (with timezone) equivalent column with an optional precision (total digits):
+
+ $table->timestampTz('added_at', $precision = 0);
+
+
+#### `timestamp()` {#collection-method}
+
+The `timestamp` method creates an `TIMESTAMP` equivalent column with an optional precision (total digits):
+
+ $table->timestamp('added_at', $precision = 0);
+
+
+#### `timestampsTz()` {#collection-method}
+
+The `timestampsTz` method creates `created_at` and `updated_at` `TIMESTAMP` (with timezone) equivalent columns with an optional precision (total digits):
+
+ $table->timestampsTz($precision = 0);
+
+
+#### `timestamps()` {#collection-method}
+
+The `timestamps` method creates `created_at` and `updated_at` `TIMESTAMP` equivalent columns with an optional precision (total digits):
+
+ $table->timestamps($precision = 0);
+
+
+#### `tinyIncrements()` {#collection-method}
+
+The `tinyIncrements` method creates an auto-incrementing `UNSIGNED TINYINT` equivalent column as a primary key:
+
+ $table->tinyIncrements('id');
+
+
+#### `tinyInteger()` {#collection-method}
+
+The `tinyInteger` method creates an `TINYINT` equivalent column:
+
+ $table->tinyInteger('votes');
+
+
+#### `unsignedBigInteger()` {#collection-method}
+
+The `unsignedBigInteger` method creates an `UNSIGNED BIGINT` equivalent column:
+
+ $table->unsignedBigInteger('votes');
+
+
+#### `unsignedDecimal()` {#collection-method}
+
+The `unsignedDecimal` method creates an `UNSIGNED DECIMAL` equivalent column with an optinoal precision (total digits) and scale (decimal digits):
+
+ $table->unsignedDecimal('amount', $precision = 8, $scale = 2);
+
+
+#### `unsignedInteger()` {#collection-method}
+
+The `unsignedInteger` method creates an `UNSIGNED INTEGER` equivalent column:
+
+ $table->unsignedInteger('votes');
+
+
+#### `unsignedMediumInteger()` {#collection-method}
+
+The `unsignedMediumInteger` method creates an `UNSIGNED MEDIUMINT` equivalent column:
+
+ $table->unsignedMediumInteger('votes');
+
+
+#### `unsignedSmallInteger()` {#collection-method}
+
+The `unsignedSmallInteger` method creates an `UNSIGNED SMALLINT` equivalent column:
+
+ $table->unsignedSmallInteger('votes');
+
+
+#### `unsignedTinyInteger()` {#collection-method}
+
+The `unsignedTinyInteger` method creates an `UNSIGNED TINYINT` equivalent column:
+
+ $table->unsignedTinyInteger('votes');
+
+
+#### `uuidMorphs()` {#collection-method}
+
+The `uuidMorphs` method is a convenience method that adds a `{column}_id` `CHAR(36)` equivalent column and a `{column}_type` `VARCHAR` equivalent column.
+
+This method is intended to be used when defining the columns necessary for a polymorphic [Eloquent relationship](/docs/{{version}}/eloquent-relationships) that use UUID identifiers. In the following example, `taggable_id` and `taggable_type` columns would be created:
+
+ $table->uuidMorphs('taggable');
+
+
+#### `uuid()` {#collection-method}
+
+The `uuid` method creates an `UUID` equivalent column:
+
+ $table->uuid('id');
+
+
+#### `year()` {#collection-method}
+
+The `year` method creates an `YEAR` equivalent column:
+
+ $table->year('birth_year');
### Column Modifiers
-In addition to the column types listed above, there are several column "modifiers" you may use while adding a column to a database table. For example, to make the column "nullable", you may use the `nullable` method:
+In addition to the column types listed above, there are several column "modifiers" you may use when adding a column to a database table. For example, to make the column "nullable", you may use the `nullable` method:
+
+ use Illuminate\Database\Schema\Blueprint;
+ use Illuminate\Support\Facades\Schema;
Schema::table('users', function (Blueprint $table) {
$table->string('email')->nullable();
});
-The following list contains all available column modifiers. This list does not include the [index modifiers](#creating-indexes):
+The following table contains all of the available column modifiers. This list does not include [index modifiers](#creating-indexes):
Modifier | Description
-------- | -----------
-`->after('column')` | Place the column "after" another column (MySQL)
-`->autoIncrement()` | Set INTEGER columns as auto-increment (primary key)
-`->charset('utf8mb4')` | Specify a character set for the column (MySQL)
-`->collation('utf8mb4_unicode_ci')` | Specify a collation for the column (MySQL/PostgreSQL/SQL Server)
-`->comment('my comment')` | Add a comment to a column (MySQL/PostgreSQL)
-`->default($value)` | Specify a "default" value for the column
-`->first()` | Place the column "first" in the table (MySQL)
-`->from($integer)` | Set the starting value of an auto-incrementing field (MySQL / PostgreSQL)
-`->nullable($value = true)` | Allows (by default) NULL values to be inserted into the column
-`->storedAs($expression)` | Create a stored generated column (MySQL)
-`->unsigned()` | Set INTEGER columns as UNSIGNED (MySQL)
-`->useCurrent()` | Set TIMESTAMP columns to use CURRENT_TIMESTAMP as default value
-`->useCurrentOnUpdate()` | Set TIMESTAMP columns to use CURRENT_TIMESTAMP when a record is updated
-`->virtualAs($expression)` | Create a virtual generated column (MySQL)
-`->generatedAs($expression)` | Create an identity column with specified sequence options (PostgreSQL)
-`->always()` | Defines the precedence of sequence values over input for an identity column (PostgreSQL)
+`->after('column')` | Place the column "after" another column (MySQL).
+`->autoIncrement()` | Set INTEGER columns as auto-incrementing (primary key).
+`->charset('utf8mb4')` | Specify a character set for the column (MySQL).
+`->collation('utf8mb4_unicode_ci')` | Specify a collation for the column (MySQL/PostgreSQL/SQL Server).
+`->comment('my comment')` | Add a comment to a column (MySQL/PostgreSQL).
+`->default($value)` | Specify a "default" value for the column.
+`->first()` | Place the column "first" in the table (MySQL).
+`->from($integer)` | Set the starting value of an auto-incrementing field (MySQL / PostgreSQL).
+`->nullable($value = true)` | Allow NULL values to be inserted into the column.
+`->storedAs($expression)` | Create a stored generated column (MySQL).
+`->unsigned()` | Set INTEGER columns as UNSIGNED (MySQL).
+`->useCurrent()` | Set TIMESTAMP columns to use CURRENT_TIMESTAMP as default value.
+`->useCurrentOnUpdate()` | Set TIMESTAMP columns to use CURRENT_TIMESTAMP when a record is updated.
+`->virtualAs($expression)` | Create a virtual generated column (MySQL).
+`->generatedAs($expression)` | Create an identity column with specified sequence options (PostgreSQL).
+`->always()` | Defines the precedence of sequence values over input for an identity column (PostgreSQL).
#### Default Expressions
-The `default` modifier accepts a value or an `\Illuminate\Database\Query\Expression` instance. Using an `Expression` instance will prevent wrapping the value in quotes and allow you to use database specific functions. One situation where this is particularly useful is when you need to assign default values to JSON columns:
+The `default` modifier accepts a value or an `Illuminate\Database\Query\Expression` instance. Using an `Expression` instance will prevent Laravel from wrapping the value in quotes and allow you to use database specific functions. One situation where this is particularly useful is when you need to assign default values to JSON columns:
{note} Support for default expressions depends on your database driver, database version, and the field type. Please refer to the appropriate documentation for compatibility. Also note that using database specific functions may tightly couple you to a specific driver.
+> {note} Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation.
### Modifying Columns
@@ -370,14 +852,14 @@ The `default` modifier accepts a value or an `\Illuminate\Database\Query\Express
#### Prerequisites
-Before modifying a column, be sure to add the `doctrine/dbal` dependency to your `composer.json` file. The Doctrine DBAL library is used to determine the current state of the column and create the SQL queries needed to make the required adjustments:
+Before modifying a column, you must install the `doctrine/dbal` package using the Composer package manager. The Doctrine DBAL library is used to determine the current state of the column and to create the SQL queries needed to make the requested changes to your column:
composer require doctrine/dbal
#### Updating Column Attributes
-The `change` method allows you to modify type and attributes of existing columns. For example, you may wish to increase the size of a `string` column. To see the `change` method in action, let's increase the size of the `name` column from 25 to 50:
+The `change` method allows you to modify the type and attributes of existing columns. For example, you may wish to increase the size of a `string` column. To see the `change` method in action, let's increase the size of the `name` column from 25 to 50. To accomplish this, we simply define the new state of the column and then call the `change` method:
Schema::table('users', function (Blueprint $table) {
$table->string('name', 50)->change();
@@ -389,12 +871,12 @@ We could also modify a column to be nullable:
$table->string('name', 50)->nullable()->change();
});
-> {note} Only the following column types can be "changed": bigInteger, binary, boolean, date, dateTime, dateTimeTz, decimal, integer, json, longText, mediumText, smallInteger, string, text, time, unsignedBigInteger, unsignedInteger, unsignedSmallInteger and uuid.
+> {note} The following column types can be modified: `bigInteger`, `binary`, `boolean`, `date`, `dateTime`, `dateTimeTz`, `decimal`, `integer`, `json`, `longText`, `mediumText`, `smallInteger`, `string`, `text`, `time`, `unsignedBigInteger`, `unsignedInteger`, `unsignedSmallInteger`, and `uuid`.
#### Renaming Columns
-To rename a column, you may use the `renameColumn` method on the schema builder. Before renaming a column, be sure to add the `doctrine/dbal` dependency to your `composer.json` file:
+To rename a column, you may use the `renameColumn` method provided by the schema builder blueprint. Before renaming a column, ensure that you have installed the `doctrine/dbal` library via the Composer package manager:
Schema::table('users', function (Blueprint $table) {
$table->renameColumn('from', 'to');
@@ -405,7 +887,7 @@ To rename a column, you may use the `renameColumn` method on the schema builder.
### Dropping Columns
-To drop a column, use the `dropColumn` method on the schema builder. Before dropping columns from a SQLite database, you will need to add the `doctrine/dbal` dependency to your `composer.json` file and run the `composer update` command in your terminal to install the library:
+To drop a column, you may use the `dropColumn` method on the schema builder blueprint. If your application is utilizing an SQLite database, you must install the `doctrine/dbal` package via the Composer package manager before the `dropColumn` method may be used:
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('votes');
@@ -422,6 +904,8 @@ You may drop multiple columns from a table by passing an array of column names t
#### Available Command Aliases
+Laravel provides several convenient methods related to dropping common types of columns. Each of these methods is described in the table below:
+
Command | Description
------- | -----------
`$table->dropMorphs('morphable');` | Drop the `morphable_id` and `morphable_type` columns.
@@ -439,9 +923,14 @@ Command | Description
The Laravel schema builder supports several types of indexes. The following example creates a new `email` column and specifies that its values should be unique. To create the index, we can chain the `unique` method onto the column definition:
- $table->string('email')->unique();
+ use Illuminate\Database\Schema\Blueprint;
+ use Illuminate\Support\Facades\Schema;
-Alternatively, you may create the index after defining the column. For example:
+ Schema::table('users', function (Blueprint $table) {
+ $table->string('email')->unique();
+ });
+
+Alternatively, you may create the index after defining the column. To do so, you should call the `unique` method on the schema builder blueprint. This methods accepts the name of the column that should receive a unique index:
$table->unique('email');
@@ -449,27 +938,27 @@ You may even pass an array of columns to an index method to create a compound (o
$table->index(['account_id', 'created_at']);
-Laravel will automatically generate an index name based on the table, column names, and the index type, but you may pass a second argument to the method to specify the index name yourself:
+When creating an index, Laravel will automatically generate an index name based on the table, column names, and the index type, but you may pass a second argument to the method to specify the index name yourself:
$table->unique('email', 'unique_email');
#### Available Index Types
-Each index method accepts an optional second argument to specify the name of the index. If omitted, the name will be derived from the names of the table and column(s) used for the index, as well as the index type.
+Laravel's schema builder blueprint class provides methods for creating each type of index supported by Laravel. Each index method accepts an optional second argument to specify the name of the index. If omitted, the name will be derived from the names of the table and column(s) used for the index, as well as the index type. Each of the available index methods are described in the table below:
Command | Description
------- | -----------
`$table->primary('id');` | Adds a primary key.
`$table->primary(['id', 'parent_id']);` | Adds composite keys.
`$table->unique('email');` | Adds a unique index.
-`$table->index('state');` | Adds a plain index.
-`$table->spatialIndex('location');` | Adds a spatial index. (except SQLite)
+`$table->index('state');` | Adds an index.
+`$table->spatialIndex('location');` | Adds a spatial index (except SQLite).
#### Index Lengths & MySQL / MariaDB
-Laravel uses the `utf8mb4` character set by default, which includes support for storing "emojis" in the database. If you are running a version of MySQL older than the 5.7.7 release or MariaDB older than the 10.2.2 release, you may need to manually configure the default string length generated by migrations in order for MySQL to create indexes for them. You may configure this by calling the `Schema::defaultStringLength` method within your `AppServiceProvider`:
+By default, Laravel uses the `utf8mb4` character set. If you are running a version of MySQL older than the 5.7.7 release or MariaDB older than the 10.2.2 release, you may need to manually configure the default string length generated by migrations in order for MySQL to create indexes for them. You may configure the default string length by calling the `Schema::defaultStringLength` method within the `boot` method of your `App\Providers\AppServiceProvider` class:
use Illuminate\Support\Facades\Schema;
@@ -488,7 +977,7 @@ Alternatively, you may enable the `innodb_large_prefix` option for your database
### Renaming Indexes
-To rename an index, you may use the `renameIndex` method. This method accepts the current index name as its first argument and the desired new name as its second argument:
+To rename an index, you may use the `renameIndex` method provided by the schema builder blueprint. This method accepts the current index name as its first argument and the desired name as its second argument:
$table->renameIndex('from', 'to')
@@ -504,7 +993,7 @@ Command | Description
`$table->dropIndex('geo_state_index');` | Drop a basic index from the "geo" table.
`$table->dropSpatialIndex('geo_location_spatialindex');` | Drop a spatial index from the "geo" table (except SQLite).
-If you pass an array of columns into a method that drops indexes, the conventional index name will be generated based on the table name, columns and key type:
+If you pass an array of columns into a method that drops indexes, the conventional index name will be generated based on the table name, columns, and index type:
Schema::table('geo', function (Blueprint $table) {
$table->dropIndex(['state']); // Drops index 'geo_state_index'
@@ -515,19 +1004,22 @@ If you pass an array of columns into a method that drops indexes, the convention
Laravel also provides support for creating foreign key constraints, which are used to force referential integrity at the database level. For example, let's define a `user_id` column on the `posts` table that references the `id` column on a `users` table:
+ use Illuminate\Database\Schema\Blueprint;
+ use Illuminate\Support\Facades\Schema;
+
Schema::table('posts', function (Blueprint $table) {
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
});
-Since this syntax is rather verbose, Laravel provides additional, terser methods that use convention to provide a better developer experience. The example above could be written like so:
+Since this syntax is rather verbose, Laravel provides additional, terser methods that use conventions to provide a better developer experience. The example above can be rewritten like so:
Schema::table('posts', function (Blueprint $table) {
$table->foreignId('user_id')->constrained();
});
-The `foreignId` method is an alias for `unsignedBigInteger` while the `constrained` method will use convention to determine the table and column name being referenced. If your table name does not match the convention, you may specify the table name by passing it as an argument to the `constrained` method:
+The `foreignId` method is an alias for `unsignedBigInteger` while the `constrained` method will use conventions to determine the table and column name being referenced. If your table name does not match Laravel's conventions, you may specify the table name by passing it as an argument to the `constrained` method:
Schema::table('posts', function (Blueprint $table) {
$table->foreignId('user_id')->constrained('users');
@@ -541,20 +1033,26 @@ You may also specify the desired action for the "on delete" and "on update" prop
->onUpdate('cascade')
->onDelete('cascade');
-Any additional [column modifiers](#column-modifiers) must be called before `constrained`:
+Any additional [column modifiers](#column-modifiers) must be called before the `constrained` method:
$table->foreignId('user_id')
->nullable()
->constrained();
-To drop a foreign key, you may use the `dropForeign` method, passing the foreign key constraint to be deleted as an argument. Foreign key constraints use the same naming convention as indexes, based on the table name and the columns in the constraint, followed by a "\_foreign" suffix:
+
+#### Dropping Foreign Keys
+
+To drop a foreign key, you may use the `dropForeign` method, passing the name of the foreign key constraint to be deleted as an argument. Foreign key constraints use the same naming convention as indexes. In other words, the foreign key constraint name is based on the name of the table and the columns in the constraint, followed by a "\_foreign" suffix:
$table->dropForeign('posts_user_id_foreign');
-Alternatively, you may pass an array containing the column name that holds the foreign key to the `dropForeign` method. The array will be automatically converted using the constraint name convention used by Laravel's schema builder:
+Alternatively, you may pass an array containing the column name that holds the foreign key to the `dropForeign` method. The array will be converted to a foreign key constraint name using Laravel's constraint naming conventions:
$table->dropForeign(['user_id']);
+
+#### Toggling Foreign Key Constraints
+
You may enable or disable foreign key constraints within your migrations by using the following methods:
Schema::enableForeignKeyConstraints();
diff --git a/mix.md b/mix.md
index a0669548202..e7d17be728c 100644
--- a/mix.md
+++ b/mix.md
@@ -4,19 +4,16 @@
- [Installation & Setup](#installation)
- [Running Mix](#running-mix)
- [Working With Stylesheets](#working-with-stylesheets)
- - [Less](#less)
- - [Sass](#sass)
- - [Stylus](#stylus)
+ - [Tailwind CSS](#tailwindcss)
- [PostCSS](#postcss)
- - [Plain CSS](#plain-css)
+ - [Sass](#sass)
- [URL Processing](#url-processing)
- [Source Maps](#css-source-maps)
- [Working With JavaScript](#working-with-scripts)
- - [Vendor Extraction](#vendor-extraction)
+ - [Vue](#vue)
- [React](#react)
- - [Vanilla JS](#vanilla-js)
+ - [Vendor Extraction](#vendor-extraction)
- [Custom Webpack Configuration](#custom-webpack-configuration)
-- [Copying Files & Directories](#copying-files-and-directories)
- [Versioning / Cache Busting](#versioning-and-cache-busting)
- [Browsersync Reloading](#browsersync-reloading)
- [Environment Variables](#environment-variables)
@@ -25,12 +22,16 @@
## Introduction
-[Laravel Mix](https://github.com/JeffreyWay/laravel-mix) provides a fluent API for defining Webpack build steps for your Laravel application using several common CSS and JavaScript pre-processors. Through simple method chaining, you can fluently define your asset pipeline. For example:
+[Laravel Mix](https://github.com/JeffreyWay/laravel-mix), a package developed by [Laracasts](https://laracasts.com) creator Jeffrey Way, provides a fluent API for defining [webpack](https://webpack.js.org) build steps for your Laravel application using several common CSS and JavaScript pre-processors.
+
+In other words, Mix makes it a cinch to compile and minify your application's CSS and JavaScript files. Through simple method chaining, you can fluently define your asset pipeline. For example:
mix.js('resources/js/app.js', 'public/js')
- .sass('resources/sass/app.scss', 'public/css');
+ .postCss('resources/css/app.css', 'public/css');
-If you've ever been confused and overwhelmed about getting started with Webpack and asset compilation, you will love Laravel Mix. However, you are not required to use it while developing your application; you are free to use any asset pipeline tool you wish, or even none at all.
+If you've ever been confused and overwhelmed about getting started with webpack and asset compilation, you will love Laravel Mix. However, you are not required to use it while developing your application; you are free to use any asset pipeline tool you wish, or even none at all.
+
+> {tip} If you need a head start building your application with Laravel and [Tailwind CSS](https://tailwindcss.com), check out one of our [application starter kits](/docs/{{version}}/starter-kits).
## Installation & Setup
@@ -38,128 +39,129 @@ If you've ever been confused and overwhelmed about getting started with Webpack
#### Installing Node
-Before triggering Mix, you must first ensure that Node.js and NPM are installed on your machine.
+Before running Mix, you must first ensure that Node.js and NPM are installed on your machine:
node -v
npm -v
-By default, Laravel Homestead includes everything you need; however, if you aren't using Vagrant, then you can easily install the latest version of Node and NPM using simple graphical installers from [their download page](https://nodejs.org/en/download/).
+You can easily install the latest version of Node and NPM using simple graphical installers from [the official Node website](https://nodejs.org/en/download/). Or, if you are using [Laravel Sail](/docs/{{version}}/sail), you may invoke Node and NPM through Sail:
+
+ ./sail node -v
+ ./sail npm -v
-
-#### Laravel Mix
+
+#### Installing Laravel Mix
-The only remaining step is to install Laravel Mix. Within a fresh installation of Laravel, you'll find a `package.json` file in the root of your directory structure. The default `package.json` file includes everything you need to get started. Think of this like your `composer.json` file, except it defines Node dependencies instead of PHP. You may install the dependencies it references by running:
+The only remaining step is to install Laravel Mix. Within a fresh installation of Laravel, you'll find a `package.json` file in the root of your directory structure. The default `package.json` file already includes everything you need to get started using Laravel Mix. Think of this file like your `composer.json` file, except it defines Node dependencies instead of PHP dependencies. You may install the dependencies it references by running:
npm install
## Running Mix
-Mix is a configuration layer on top of [Webpack](https://webpack.js.org), so to run your Mix tasks you only need to execute one of the NPM scripts that is included with the default Laravel `package.json` file:
+Mix is a configuration layer on top of [webpack](https://webpack.js.org), so to run your Mix tasks you only need to execute one of the NPM scripts that is included in the default Laravel `package.json` file. When you run the `dev` or `production` scripts, all of your application's CSS and JavaScript assets will be compiled and placed in your application's `public` directory:
// Run all Mix tasks...
npm run dev
// Run all Mix tasks and minify output...
- npm run production
+ npm run prod
#### Watching Assets For Changes
-The `npm run watch` command will continue running in your terminal and watch all relevant files for changes. Webpack will then automatically recompile your assets when it detects a change:
+The `npm run watch` command will continue running in your terminal and watch all relevant CSS and JavaScript files for changes. Webpack will automatically recompile your assets when it detects a change to one of these files:
npm run watch
-You may find that in certain environments Webpack isn't updating when your files change. If this is the case on your system, consider using the `watch-poll` command:
+Webpack may not be able to detect your file changes in certain local development environments. If this is the case on your system, consider using the `watch-poll` command:
npm run watch-poll
## Working With Stylesheets
-The `webpack.mix.js` file is your entry point for all asset compilation. Think of it as a light configuration wrapper around Webpack. Mix tasks can be chained together to define exactly how your assets should be compiled.
+Your application's `webpack.mix.js` file is your entry point for all asset compilation. Think of it as a light configuration wrapper around [webpack](https://webpack.js.org). Mix tasks can be chained together to define exactly how your assets should be compiled.
-
-### Less
+
+### Tailwind CSS
-The `less` method may be used to compile [Less](http://lesscss.org/) into CSS. Let's compile our primary `app.less` file to `public/css/app.css`.
+[Tailwind CSS](https://tailwindcss.com) is a modern, utility-first framework for building amazing sites without ever leaving your HTML. Let's dig into how to start using it in a Laravel project with Laravel Mix. First, we should install Tailwind using NPM and generate our Tailwind configuration file:
- mix.less('resources/less/app.less', 'public/css');
+ npm install tailwindcss@compat
-Multiple calls to the `less` method may be used to compile multiple files:
+ npx tailwindcss init
- mix.less('resources/less/app.less', 'public/css')
- .less('resources/less/admin.less', 'public/css');
+The `init` command will generate a `tailwind.config.js` file. Within this file, you may configure the paths to all of your application's templates and JavaScript so that Tailwind can tree-shake unused styles when optimizing your CSS for production:
-If you wish to customize the file name of the compiled CSS, you may pass a full file path as the second argument to the `less` method:
+```js
+purge: [
+ './storage/framework/views/*.php',
+ './resources/**/*.blade.php',
+ './resources/**/*.js',
+ './resources/**/*.vue',
+],
+```
- mix.less('resources/less/app.less', 'public/stylesheets/styles.css');
+Next, you should add each of Tailwind's "layers" to your application's `resources/css/app.css` file:
-If you need to override the [underlying Less plug-in options](https://github.com/webpack-contrib/less-loader#options), you may pass an object as the third argument to `mix.less()`:
+```css
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+```
- mix.less('resources/less/app.less', 'public/css', {
- strictMath: true
- });
+Once you have configured Tailwind's layers, you are ready to update your application's `webpack.mix.js` file to compile your Tailwind powered CSS:
-
-### Sass
-
-The `sass` method allows you to compile [Sass](https://sass-lang.com/) into CSS. You may use the method like so:
-
- mix.sass('resources/sass/app.scss', 'public/css');
+```js
+mix.js('resources/js/app.js', 'public/js')
+ .postCss('resources/css/app.css', 'public/css', [
+ require('postcss-import'),
+ require('tailwindcss'),
+ ]);
+```
-Again, like the `less` method, you may compile multiple Sass files into their own respective CSS files and even customize the output directory of the resulting CSS:
+Finally, you should reference your stylesheet in your application's primary layout template. Many applications choose to store this template at `resources/views/layouts/app.blade.php`. In addition, ensure you add the responsive viewport `meta` tag if it's not already present:
- mix.sass('resources/sass/app.sass', 'public/css')
- .sass('resources/sass/admin.sass', 'public/css/admin');
+```html
+
+
+
+
+
+```
-Additional [Node-Sass plug-in options](https://github.com/sass/node-sass#options) may be provided as the third argument:
-
- mix.sass('resources/sass/app.sass', 'public/css', {
- precision: 5
- });
-
-
-### Stylus
+
+### PostCSS
-Similar to Less and Sass, the `stylus` method allows you to compile [Stylus](http://stylus-lang.com/) into CSS:
+[PostCSS](https://postcss.org/), a powerful tool for transforming your CSS, is included with Laravel Mix out of the box. By default, Mix leverages the popular [Autoprefixer](https://github.com/postcss/autoprefixer) plugin to automatically apply all necessary CSS3 vendor prefixes. However, you're free to add any additional plugins that are appropriate for your application.
- mix.stylus('resources/stylus/app.styl', 'public/css');
+First, install the desired plugin through NPM and include it in your array of plugins when calling Mix's `postCss` method. The `postCss` method accepts the path to your CSS file as its first argument and the directory where the compiled file should be placed as its second argument:
-You may also install additional Stylus plug-ins, such as [Rupture](https://github.com/jescalan/rupture). First, install the plug-in in question through NPM (`npm install rupture`) and then require it in your call to `mix.stylus()`:
+ mix.postCss('resources/css/app.css', 'public/css', [
+ require('postcss-custom-properties')
+ ]);
- mix.stylus('resources/stylus/app.styl', 'public/css', {
- use: [
- require('rupture')()
- ]
- });
+Or, you may execute `postCss` with no additional plugins in order to achieve simple CSS compilation and minification:
-
-### PostCSS
+ mix.postCss('resources/css/app.css', 'public/css');
-[PostCSS](https://postcss.org/), a powerful tool for transforming your CSS, is included with Laravel Mix out of the box. By default, Mix leverages the popular [Autoprefixer](https://github.com/postcss/autoprefixer) plug-in to automatically apply all necessary CSS3 vendor prefixes. However, you're free to add any additional plug-ins that are appropriate for your application. First, install the desired plug-in through NPM and then reference it in your `webpack.mix.js` file:
+
+### Sass
- mix.sass('resources/sass/app.scss', 'public/css')
- .options({
- postCss: [
- require('postcss-css-variables')()
- ]
- });
+The `sass` method allows you to compile [Sass](https://sass-lang.com/) into CSS that can be understood by web browsers. The `sass` method accepts the path to your Sass file as its first argument and the directory where the compiled file should be placed as its second argument:
-
-### Plain CSS
+ mix.sass('resources/sass/app.scss', 'public/css');
-If you would just like to concatenate some plain CSS stylesheets into a single file, you may use the `styles` method.
+Y may compile multiple Sass files into their own respective CSS files and even customize the output directory of the resulting CSS by calling the `sass` method multiple times:
- mix.styles([
- 'public/css/vendor/normalize.css',
- 'public/css/vendor/videojs.css'
- ], 'public/css/all.css');
+ mix.sass('resources/sass/app.sass', 'public/css')
+ .sass('resources/sass/admin.sass', 'public/css/admin');
### URL Processing
-Because Laravel Mix is built on top of Webpack, it's important to understand a few Webpack concepts. For CSS compilation, Webpack will rewrite and optimize any `url()` calls within your stylesheets. While this might initially sound strange, it's an incredibly powerful piece of functionality. Imagine that we want to compile Sass that includes a relative URL to an image:
+Because Laravel Mix is built on top of webpack, it's important to understand a few webpack concepts. For CSS compilation, webpack will rewrite and optimize any `url()` calls within your stylesheets. While this might initially sound strange, it's an incredibly powerful piece of functionality. Imagine that we want to compile Sass that includes a relative URL to an image:
.example {
background: url('../images/example.png');
@@ -167,7 +169,7 @@ Because Laravel Mix is built on top of Webpack, it's important to understand a f
> {note} Absolute paths for any given `url()` will be excluded from URL-rewriting. For example, `url('/images/thing.png')` or `url('http://example.com/images/thing.png')` won't be modified.
-By default, Laravel Mix and Webpack will find `example.png`, copy it to your `public/images` folder, and then rewrite the `url()` within your generated stylesheet. As such, your compiled CSS will be:
+By default, Laravel Mix and webpack will find `example.png`, copy it to your `public/images` folder, and then rewrite the `url()` within your generated stylesheet. As such, your compiled CSS will be:
.example {
background: url(/images/example.png?d41d8cd98f00b204e9800998ecf8427e);
@@ -175,10 +177,9 @@ By default, Laravel Mix and Webpack will find `example.png`, copy it to your `pu
As useful as this feature may be, it's possible that your existing folder structure is already configured in a way you like. If this is the case, you may disable `url()` rewriting like so:
- mix.sass('resources/sass/app.scss', 'public/css')
- .options({
- processCssUrls: false
- });
+ mix.sass('resources/sass/app.scss', 'public/css').options({
+ processCssUrls: false
+ });
With this addition to your `webpack.mix.js` file, Mix will no longer match any `url()` or copy assets to your public directory. In other words, the compiled CSS will look just like how you originally typed it:
@@ -189,7 +190,7 @@ With this addition to your `webpack.mix.js` file, Mix will no longer match any `
### Source Maps
-Though disabled by default, source maps may be activated by calling the `mix.sourceMaps()` method in your `webpack.mix.js` file. Though it comes with a compile/performance cost, this will provide extra debugging information to your browser's developer tools when using compiled assets.
+Though disabled by default, source maps may be activated by calling the `mix.sourceMaps()` method in your `webpack.mix.js` file. Though it comes with a compile/performance cost, this will provide extra debugging information to your browser's developer tools when using compiled assets:
mix.js('resources/js/app.js', 'public/js')
.sourceMaps();
@@ -216,21 +217,54 @@ With this single line of code, you may now take advantage of:
- ES2015 syntax.
- Modules
-- Compilation of `.vue` files.
- Minification for production environments.
+
+### Vue
+
+Mix will automatically install the Babel plugins necessary for Vue single-file component compilation support when using the `js` method. No further configuration is required:
+
+ mix.js('resources/js/app.js', 'public/js');
+
+Once your JavaScript has been compiled, you can reference it in your application:
+
+```html
+
+
+
+
+
+```
+
+
+### React
+
+Mix can automatically install the Babel plugins necessary for React support. To get started, replace your call to Mix's `js` method with a call to the `react` method:
+
+ mix.react('resources/js/app.jsx', 'public/js');
+
+Behind the scenes, Mix will download and include the appropriate `babel-preset-react` Babel plugin. Once your JavaScript has been compiled, you can reference it in your application:
+
+```html
+
+
+
+
+
+```
+
### Vendor Extraction
-One potential downside to bundling all application-specific JavaScript with your vendor libraries is that it makes long-term caching more difficult. For example, a single update to your application code will force the browser to re-download all of your vendor libraries even if they haven't changed.
+One potential downside to bundling all of your application-specific JavaScript with your vendor libraries such as React and Vue is that it makes long-term caching more difficult. For example, a single update to your application code will force the browser to re-download all of your vendor libraries even if they haven't changed.
If you intend to make frequent updates to your application's JavaScript, you should consider extracting all of your vendor libraries into their own file. This way, a change to your application code will not affect the caching of your large `vendor.js` file. Mix's `extract` method makes this a breeze:
mix.js('resources/js/app.js', 'public/js')
.extract(['vue'])
-The `extract` method accepts an array of all libraries or modules that you wish to extract into a `vendor.js` file. Using the above snippet as an example, Mix will generate the following files:
+The `extract` method accepts an array of all libraries or modules that you wish to extract into a `vendor.js` file. Using the snippet above as an example, Mix will generate the following files:
- `public/js/manifest.js`: *The Webpack manifest runtime*
@@ -244,33 +278,10 @@ To avoid JavaScript errors, be sure to load these files in the proper order:
-
-### React
-
-Mix can automatically install the Babel plug-ins necessary for React support. To get started, replace your `mix.js()` call with `mix.react()`:
-
- mix.react('resources/js/app.jsx', 'public/js');
-
-Behind the scenes, Mix will download and include the appropriate `babel-preset-react` Babel plug-in.
-
-
-### Vanilla JS
-
-Similar to combining stylesheets with `mix.styles()`, you may also combine and minify any number of JavaScript files with the `scripts()` method:
-
- mix.scripts([
- 'public/js/admin.js',
- 'public/js/dashboard.js'
- ], 'public/js/all.js');
-
-This option is particularly useful for legacy projects where you don't require Webpack compilation for your JavaScript.
-
-> {tip} A slight variation of `mix.scripts()` is `mix.babel()`. Its method signature is identical to `scripts`; however, the concatenated file will receive Babel compilation, which translates any ES2015 code to vanilla JavaScript that all browsers will understand.
-
### Custom Webpack Configuration
-Behind the scenes, Laravel Mix references a pre-configured `webpack.config.js` file to get you up and running as quickly as possible. Occasionally, you may need to manually modify this file. You might have a special loader or plug-in that needs to be referenced, or maybe you prefer to use Stylus instead of Sass. In such instances, you have two choices:
+Behind the scenes, Laravel Mix references a pre-configured `webpack.config.js` file to get you up and running as quickly as possible. Occasionally, you may need to manually modify this file. For example, you might have a special loader or plugin that needs to be referenced. In such instances, you have two choices:
#### Merging Custom Configuration
@@ -290,32 +301,21 @@ Mix provides a useful `webpackConfig` method that allows you to merge any short
If you would like to completely customize your Webpack configuration, copy the `node_modules/laravel-mix/setup/webpack.config.js` file to your project's root directory. Next, point all of the `--config` references in your `package.json` file to the newly copied configuration file. If you choose to take this approach to customization, any future upstream updates to Mix's `webpack.config.js` must be manually merged into your customized file.
-
-## Copying Files & Directories
-
-The `copy` method may be used to copy files and directories to new locations. This can be useful when a particular asset within your `node_modules` directory needs to be relocated to your `public` folder.
-
- mix.copy('node_modules/foo/bar.css', 'public/css/bar.css');
-
-When copying a directory, the `copy` method will flatten the directory's structure. To maintain the directory's original structure, you should use the `copyDirectory` method instead:
-
- mix.copyDirectory('resources/img', 'public/img');
-
## Versioning / Cache Busting
-Many developers suffix their compiled assets with a timestamp or unique token to force browsers to load the fresh assets instead of serving stale copies of the code. Mix can handle this for you using the `version` method.
+Many developers suffix their compiled assets with a timestamp or unique token to force browsers to load the fresh assets instead of serving stale copies of the code. Mix can automatically handle this for you using the `version` method.
-The `version` method will automatically append a unique hash to the filenames of all compiled files, allowing for more convenient cache busting:
+The `version` method will append a unique hash to the filenames of all compiled files, allowing for more convenient cache busting:
mix.js('resources/js/app.js', 'public/js')
.version();
-After generating the versioned file, you won't know the exact file name. So, you should use Laravel's global `mix` function within your [views](/docs/{{version}}/views) to load the appropriately hashed asset. The `mix` function will automatically determine the current name of the hashed file:
+After generating the versioned file, you won't know the exact filename. So, you should use Laravel's global `mix` function within your [views](/docs/{{version}}/views) to load the appropriately hashed asset. The `mix` function will automatically determine the current name of the hashed file:
-Because versioned files are usually unnecessary in development, you may instruct the versioning process to only run during `npm run production`:
+Because versioned files are usually unnecessary in development, you may instruct the versioning process to only run during `npm run prod`:
mix.js('resources/js/app.js', 'public/js');
@@ -326,44 +326,49 @@ Because versioned files are usually unnecessary in development, you may instruct
#### Custom Mix Base URLs
-If your Mix compiled assets are deployed to a CDN separate from your application, you will need to change the base URL generated by the `mix` function. You may do so by adding a `mix_url` configuration option to your `config/app.php` configuration file:
+If your Mix compiled assets are deployed to a CDN separate from your application, you will need to change the base URL generated by the `mix` function. You may do so by adding a `mix_url` configuration option to your application's `config/app.php` configuration file:
'mix_url' => env('MIX_ASSET_URL', null)
After configuring the Mix URL, The `mix` function will prefix the configured URL when generating URLs to assets:
- https://cdn.example.com/js/app.js?id=1964becbdd96414518cd
+```bash
+https://cdn.example.com/js/app.js?id=1964becbdd96414518cd
+```
## Browsersync Reloading
-[BrowserSync](https://browsersync.io/) can automatically monitor your files for changes, and inject your changes into the browser without requiring a manual refresh. You may enable support by calling the `mix.browserSync()` method:
+[BrowserSync](https://browsersync.io/) can automatically monitor your files for changes, and inject your changes into the browser without requiring a manual refresh. You may enable support for this by calling the `mix.browserSync()` method:
- mix.browserSync('my-domain.test');
+```js
+mix.browserSync('laravel.test');
+```
- // Or...
+[BrowserSync options](https://browsersync.io/docs/options) may be specified by passing a JavaScript object to the `browserSync` method:
- // https://browsersync.io/docs/options
- mix.browserSync({
- proxy: 'my-domain.test'
- });
+```js
+mix.browserSync({
+ proxy: 'laravel.test'
+});
+```
-You may pass either a string (proxy) or object (BrowserSync settings) to this method. Next, start Webpack's dev server using the `npm run watch` command. Now, when you modify a script or PHP file, watch as the browser instantly refreshes the page to reflect your changes.
+Next, start webpack's development server using the `npm run watch` command. Now, when you modify a script or PHP file you can watch as the browser instantly refreshes the page to reflect your changes.
## Environment Variables
-You may inject environment variables into Mix by prefixing a key in your `.env` file with `MIX_`:
+You may inject environment variables into your `webpack.mix.js` script by prefixing one of the environment variables in your `.env` file with `MIX_`:
MIX_SENTRY_DSN_PUBLIC=http://example.com
-After the variable has been defined in your `.env` file, you may access it via the `process.env` object. If the value changes while you are running a `watch` task, you will need to restart the task:
+After the variable has been defined in your `.env` file, you may access it via the `process.env` object. However, you will need to restart the task if the environment variable's value changes while the task is running:
process.env.MIX_SENTRY_DSN_PUBLIC
## Notifications
-When available, Mix will automatically display OS notifications for each bundle. This will give you instant feedback, as to whether the compilation was successful or not. However, there may be instances when you'd prefer to disable these notifications. One such example might be triggering Mix on your production server. Notifications may be deactivated, via the `disableNotifications` method.
+When available, Mix will automatically display OS notifications when compiling, giving you instant feedback as to whether the compilation was successful or not. However, there may be instances when you would prefer to disable these notifications. One such example might be triggering Mix on your production server. Notifications may be deactivated using the `disableNotifications` method:
mix.disableNotifications();
diff --git a/mocking.md b/mocking.md
index 916c7334cdc..86ecd36a8ee 100644
--- a/mocking.md
+++ b/mocking.md
@@ -2,41 +2,50 @@
- [Introduction](#introduction)
- [Mocking Objects](#mocking-objects)
+- [Mocking Facades](#mocking-facades)
- [Bus Fake](#bus-fake)
+ - [Job Chains](#bus-job-chains)
+ - [Job Batches](#job-batches)
- [Event Fake](#event-fake)
- [Scoped Event Fakes](#scoped-event-fakes)
- [HTTP Fake](#http-fake)
- [Mail Fake](#mail-fake)
- [Notification Fake](#notification-fake)
- [Queue Fake](#queue-fake)
+ - [Job Chains](#job-chains)
- [Storage Fake](#storage-fake)
- [Interacting With Time](#interacting-with-time)
-- [Facades](#mocking-facades)
## Introduction
-When testing Laravel applications, you may wish to "mock" certain aspects of your application so they are not actually executed during a given test. For example, when testing a controller that dispatches an event, you may wish to mock the event listeners so they are not actually executed during the test. This allows you to only test the controller's HTTP response without worrying about the execution of the event listeners, since the event listeners can be tested in their own test case.
+When testing Laravel applications, you may wish to "mock" certain aspects of your application so they are not actually executed during a given test. For example, when testing a controller that dispatches an event, you may wish to mock the event listeners so they are not actually executed during the test. This allows you to only test the controller's HTTP response without worrying about the execution of the event listeners since the event listeners can be tested in their own test case.
-Laravel provides helpers for mocking events, jobs, and facades out of the box. These helpers primarily provide a convenience layer over Mockery so you do not have to manually make complicated Mockery method calls. You can also use [Mockery](http://docs.mockery.io/en/latest/) or PHPUnit to create your own mocks or spies.
+Laravel provides helpful methods for mocking events, jobs, and other facades out of the box. These helpers primarily provide a convenience layer over Mockery so you do not have to manually make complicated Mockery method calls.
## Mocking Objects
-When mocking an object that is going to be injected into your application via Laravel's service container, you will need to bind your mocked instance into the container as an `instance` binding. This will instruct the container to use your mocked instance of the object instead of constructing the object itself:
+When mocking an object that is going to be injected into your application via Laravel's [service container](/docs/{{version}}/container), you will need to bind your mocked instance into the container as an `instance` binding. This will instruct the container to use your mocked instance of the object instead of constructing the object itself:
use App\Service;
use Mockery;
- $this->instance(Service::class, Mockery::mock(Service::class, function ($mock) {
- $mock->shouldReceive('process')->once();
- }));
+ public function test_something_can_be_mocked()
+ {
+ $this->instance(
+ Service::class,
+ Mockery::mock(Service::class, function ($mock) {
+ $mock->shouldReceive('process')->once();
+ })
+ );
+ }
-In order to make this more convenient, you may use the `mock` method, which is provided by Laravel's base test case class:
+In order to make this more convenient, you may use the `mock` method that is provided by Laravel's base test case class. For example, the following example is equivalent to the example above:
use App\Service;
- $this->mock(Service::class, function ($mock) {
+ $mock = $this->mock(Service::class, function ($mock) {
$mock->shouldReceive('process')->once();
});
@@ -44,22 +53,80 @@ You may use the `partialMock` method when you only need to mock a few methods of
use App\Service;
- $this->partialMock(Service::class, function ($mock) {
+ $mock = $this->partialMock(Service::class, function ($mock) {
$mock->shouldReceive('process')->once();
});
-Similarly, if you want to spy on an object, Laravel's base test case class offers a `spy` method as a convenient wrapper around the `Mockery::spy` method:
+Similarly, if you want to [spy](http://docs.mockery.io/en/latest/reference/spies.html) on an object, Laravel's base test case class offers a `spy` method as a convenient wrapper around the `Mockery::spy` method:
use App\Service;
- $this->spy(Service::class, function ($mock) {
- $mock->shouldHaveReceived('process');
- });
+ $spy = $this->spy(Service::class;
+
+ // ...
+
+ $spy->shouldHaveReceived('process')
+
+
+## Mocking Facades
+
+Unlike traditional static method calls, [facades](/docs/{{version}}/facades) may be mocked. This provides a great advantage over traditional static methods and grants you the same testability that you would have if you were using traditional dependency injection. When testing, you may often want to mock a call to a Laravel facade that occurs in one of your controllers. For example, consider the following controller action:
+
+ once()
+ ->with('key')
+ ->andReturn('value');
+
+ $response = $this->get('/users');
+
+ // ...
+ }
+ }
+
+> {note} You should not mock the `Request` facade. Instead, pass the input you desire into the [HTTP testing methods](/docs/{{version}}/http-tests) such as `get` and `post` when running your test. Likewise, instead of mocking the `Config` facade, call the `Config::set` method in your tests.
## Bus Fake
-As an alternative to mocking, you may use the `Bus` facade's `fake` method to prevent jobs from being dispatched. When using fakes, assertions are made after the code under test is executed:
+When testing code that dispatches jobs, you typically want to assert that a given job was dispatched but not actually queue or execute the job. This is because the job's execution can normally be tested in a separate test class.
+
+You may use the `Bus` facade's `fake` method to prevent jobs from being dispatched to the queue. Then, after executing the code under test, you may inspect which jobs the application attempted to dispatch using the `assertDispatched` and `assertNotDispatched` methods:
order->id === $order->id;
- });
+ // Assert that a job was dispatched...
+ Bus::assertDispatched(ShipOrder::class);
// Assert a job was not dispatched...
Bus::assertNotDispatched(AnotherJob::class);
}
}
+You may pass a closure to the `assertDispatched` or `assertNotDispatched` methods in order to assert that a job was dispatched that passes a given "truth test". If at least one job was dispatched that passes the given truth test then the assertion will be successful. For example, you may wish to assert that a job was dispatched for a specific order:
+
+ Bus::assertDispatched(function (ShipOrder $job) use ($order) {
+ return $job->order->id === $order->id;
+ });
+
+
+### Job Chains
+
+The `Bus` facade's `assertChained` method may be used to assert that a [chain of jobs](/docs/{{version}}/queues#job-chaining) was dispatched. The `assertChained` method accepts an array of chained jobs as its first argument:
+
+ use App\Jobs\RecordShipment;
+ use App\Jobs\ShipOrder;
+ use App\Jobs\UpdateInventory;
+ use Illuminate\Support\Facades\Bus;
+
+ Bus::assertChained([
+ ShipOrder::class,
+ RecordShipment::class,
+ UpdateInventory::class
+ ]);
+
+As you can see in the example above, the array of chained jobs may be an array of the job's class names. However, you may also provide an array of actual job instances. When doing so, Laravel will ensure that the job instances are of the same class and have the same property values of the chained jobs dispatched by your application:
+
+ Bus::assertChained([
+ new ShipOrder,
+ new RecordShipment,
+ new UpdateInventory,
+ ]);
+
+
+### Job Batches
+
+The `Bus` facade's `assertBatched` method may be used to assert that a [batch of jobs](/docs/{{version}}/queues#job-batches) was dispatched. The closure given to the `assertBatched` method receives an instance of `Illuminate\Bus\PendingBatch`, which may be used to inspect the jobs within the batch:
+
+ use Illuminate\Bus\PendingBatch;
+ use Illuminate\Support\Facades\Bus;
+
+ Bus::assertBatched(function (PendingBatch $batch) {
+ return $batch->name == 'import-csv' &&
+ $batch->jobs->count() === 10;
+ });
+
## Event Fake
-As an alternative to mocking, you may use the `Event` facade's `fake` method to prevent all event listeners from executing. You may then assert that events were dispatched and even inspect the data they received. When using fakes, assertions are made after the code under test is executed:
+When testing code that dispatches events, you may wish to instruct Laravel to not actually execute the event's listeners. Using the `Event` facade's `fake` method, you may prevent listeners from executing, execute the code under test, and then assert which events were dispatched by your application using the `assertDispatched` and `assertNotDispatched` methods:
order->id === $order->id;
- });
+ // Assert that an event was dispatched...
+ Event::assertDispatched(OrderShipped::class);
// Assert an event was dispatched twice...
Event::assertDispatched(OrderShipped::class, 2);
@@ -129,6 +235,12 @@ As an alternative to mocking, you may use the `Event` facade's `fake` method to
}
}
+You may pass a closure to the `assertDispatched` or `assertNotDispatched` methods in order to assert that an event was dispatched that passes a given "truth test". If at least one event was dispatched that passes the given truth test then the assertion will be successful:
+
+ Event::assertDispatched(function (OrderShipped $event) use ($order) {
+ return $event->order->id === $order->id;
+ });
+
> {note} After calling `Event::fake()`, no event listeners will be executed. So, if your tests use model factories that rely on events, such as creating a UUID during a model's `creating` event, you should call `Event::fake()` **after** using your factories.
@@ -139,7 +251,7 @@ If you only want to fake event listeners for a specific set of events, you may p
/**
* Test order process.
*/
- public function testOrderProcess()
+ public function test_orders_can_be_processed()
{
Event::fake([
OrderCreated::class,
@@ -174,7 +286,7 @@ If you only want to fake event listeners for a portion of your test, you may use
/**
* Test order process.
*/
- public function testOrderProcess()
+ public function test_orders_can_be_processed()
{
$order = Event::fakeFor(function () {
$order = Order::factory()->create();
@@ -197,7 +309,9 @@ The `Http` facade's `fake` method allows you to instruct the HTTP client to retu
## Mail Fake
-You may use the `Mail` facade's `fake` method to prevent mail from being sent. You may then assert that [mailables](/docs/{{version}}/mail) were sent to users and even inspect the data they received. When using fakes, assertions are made after the code under test is executed:
+You may use the `Mail` facade's `fake` method to prevent mail from being sent. Typically, sending mail is unrelated to the code you are actually testing. Most likely, it is sufficient to simply assert that Laravel was instructed to send a given mailable.
+
+After calling the `Mail` facade's `fake` method, you may then assert that [mailables](/docs/{{version}}/mail) were instructed to be sent to users and even inspect the data the mailables received:
order->id === $order->id;
- });
+ // Assert that no mailables were sent...
+ Mail::assertNothingSent();
- // Assert a message was sent to the given users...
- Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
- return $mail->hasTo($user->email) &&
- $mail->hasCc('...') &&
- $mail->hasBcc('...');
- });
+ // Assert that a mailable was sent...
+ Mail::assertSent(OrderShipped::class);
// Assert a mailable was sent twice...
Mail::assertSent(OrderShipped::class, 2);
@@ -242,13 +347,30 @@ You may use the `Mail` facade's `fake` method to prevent mail from being sent. Y
If you are queueing mailables for delivery in the background, you should use the `assertQueued` method instead of `assertSent`:
- Mail::assertQueued(...);
- Mail::assertNotQueued(...);
+ Mail::assertQueued(OrderShipped::class);
+
+ Mail::assertNotQueued(OrderShipped::class);
+
+You may pass a closure to the `assertSent` or `assertNotSent` methods in order to assert that a mailable was sent that passes a given "truth test". If at least one mailable was sent that passes the given truth test then the assertion will be successful:
+
+ Mail::assertSent(function (OrderShipped $mail) use ($order) {
+ return $mail->order->id === $order->id;
+ });
+
+When calling the `Mail` facade's assertion methods, the mailable instance accepted by the provided closure exposes helpful methods for examining the recipients of the mailable:
+
+ Mail::assertSent(OrderShipped::class, function ($mail) use ($user) {
+ return $mail->hasTo($user->email) &&
+ $mail->hasCc('...') &&
+ $mail->hasBcc('...');
+ });
## Notification Fake
-You may use the `Notification` facade's `fake` method to prevent notifications from being sent. You may then assert that [notifications](/docs/{{version}}/notifications) were sent to users and even inspect the data they received. When using fakes, assertions are made after the code under test is executed:
+You may use the `Notification` facade's `fake` method to prevent notifications from being sent. Typically, sending notifications is unrelated to the code you are actually testing. Most likely, it is sufficient to simply assert that Laravel was instructed to send a given notification.
+
+After calling the `Notification` facade's `fake` method, you may then assert that [notifications](/docs/{{version}}/notifications) were instructed to be sent to users and even inspect the data the notifications received:
order->id === $order->id;
- }
- );
+ // Assert that no notifications were sent...
+ Notification::assertNothingSent();
// Assert a notification was sent to the given users...
Notification::assertSentTo(
@@ -289,27 +402,45 @@ You may use the `Notification` facade's `fake` method to prevent notifications f
Notification::assertNotSentTo(
[$user], AnotherNotification::class
);
+ }
+ }
- // Assert a notification was sent via Notification::route() method...
- Notification::assertSentTo(
- new AnonymousNotifiable, OrderShipped::class
- );
+You may pass a closure to the `assertSentTo` or `assertNotSentTo` methods in order to assert that a notification was sent that passes a given "truth test". If at least one notification was sent that passes the given truth test then the assertion will be successful:
- // Assert Notification::route() method sent notification to the correct user...
- Notification::assertSentTo(
- new AnonymousNotifiable,
- OrderShipped::class,
- function ($notification, $channels, $notifiable) use ($user) {
- return $notifiable->routes['mail'] === $user->email;
- }
- );
+ Notification::assertSentTo(
+ $user,
+ function (OrderShipped $notification, $channels) use ($order) {
+ return $notification->order->id === $order->id;
}
- }
+ );
+
+
+#### On-Demand Notifications
+
+If the code you are testing sends [on-demand notifications](/docs/{{version}}/notifications#on-demand-notifications), you will need to assert that the notification was sent to an `Illuminate\Notifications\AnonymousNotifiable` instance:
+
+ use Illuminate\Notifications\AnonymousNotifiable;
+
+ Notification::assertSentTo(
+ new AnonymousNotifiable, OrderShipped::class
+ );
+
+By passing a closure as the third argument to the notification assertion methods, you may determine if an on-demand notification was sent to the correct "route" address:
+
+ Notification::assertSentTo(
+ new AnonymousNotifiable,
+ OrderShipped::class,
+ function ($notification, $channels, $notifiable) use ($user) {
+ return $notifiable->routes['mail'] === $user->email;
+ }
+ );
## Queue Fake
-As an alternative to mocking, you may use the `Queue` facade's `fake` method to prevent jobs from being queued. You may then assert that jobs were pushed to the queue and even inspect the data they received. When using fakes, assertions are made after the code under test is executed:
+You may use the `Queue` facade's `fake` method to prevent queued jobs from being pushed to the queue. Most likely, it is sufficient to simply assert that Laravel was instructed to push a given job to the queue since the queued jobs themselves may be tested in another test class.
+
+After calling the `Queue` facade's `fake` method, you may then assert that the application attempted to push jobs to the queue:
order->id === $order->id;
- });
+ // Assert that no jobs were pushed...
+ Queue::assertNothingPushed();
// Assert a job was pushed to a given queue...
Queue::assertPushedOn('queue-name', ShipOrder::class);
@@ -347,28 +473,45 @@ As an alternative to mocking, you may use the `Queue` facade's `fake` method to
// Assert a job was not pushed...
Queue::assertNotPushed(AnotherJob::class);
+ }
+ }
- // Assert a job was pushed with a given chain of jobs, matching by class...
- Queue::assertPushedWithChain(ShipOrder::class, [
- AnotherJob::class,
- FinalJob::class
- ]);
+You may pass a closure to the `assertPushed` or `assertNotPushed` methods in order to assert that a job was pushed that passes a given "truth test". If at least one job was pushed that passes the given truth test then the assertion will be successful:
- // Assert a job was pushed with a given chain of jobs, matching by both class and properties...
- Queue::assertPushedWithChain(ShipOrder::class, [
- new AnotherJob('foo'),
- new FinalJob('bar'),
- ]);
+ Queue::assertPushed(function (ShipOrder $job) use ($order) {
+ return $job->order->id === $order->id;
+ });
- // Assert a job was pushed without a chain of jobs...
- Queue::assertPushedWithoutChain(ShipOrder::class);
- }
- }
+
+### Job Chains
+
+The `Queue` facade's `assertPushedWithChain` and `assertPushedWithoutChain` methods may be used to inspect the job chain of a pushed job. The `assertPushedWithChain` method accepts the primary job as its first argument and an array of chained jobs as its second argument:
+
+ use App\Jobs\RecordShipment;
+ use App\Jobs\ShipOrder;
+ use App\Jobs\UpdateInventory;
+ use Illuminate\Support\Facades\Queue;
+
+ Queue::assertPushedWithChain(ShipOrder::class, [
+ RecordShipment::class,
+ UpdateInventory::class
+ ]);
+
+As you can see in the example above, the array of chained jobs may be an array of the job's class names. However, you may also provide an array of actual job instances. When doing so, Laravel will ensure that the job instances are of the same class and have the same property values of the chained jobs dispatched by your application:
+
+ Queue::assertPushedWithChain(ShipOrder::class, [
+ new RecordShipment,
+ new UpdateInventory,
+ ]);
+
+You may use the `assertPushedWithoutChain` method to assert that a job was pushed without a chain of jobs:
+
+ Queue::assertPushedWithoutChain(ShipOrder::class);
## Storage Fake
-The `Storage` facade's `fake` method allows you to easily generate a fake disk that, combined with the file generation utilities of the `UploadedFile` class, greatly simplifies the testing of file uploads. For example:
+The `Storage` facade's `fake` method allows you to easily generate a fake disk that, combined with the file generation utilities of the `Illuminate\Http\UploadedFile` class, greatly simplifies the testing of file uploads. For example:
{tip} By default, the `fake` method will delete all files in its temporary directory. If you would like to keep these files, you may use the "persistentFake" method instead.
@@ -428,57 +573,3 @@ When testing, you may occasionally need to modify the time returned by helpers s
// Return back to the present time...
$this->travelBack();
}
-
-
-## Facades
-
-Unlike traditional static method calls, [facades](/docs/{{version}}/facades) may be mocked. This provides a great advantage over traditional static methods and grants you the same testability you would have if you were using dependency injection. When testing, you may often want to mock a call to a Laravel facade in one of your controllers. For example, consider the following controller action:
-
- once()
- ->with('key')
- ->andReturn('value');
-
- $response = $this->get('/users');
-
- // ...
- }
- }
-
-> {note} You should not mock the `Request` facade. Instead, pass the input you desire into the HTTP helper methods such as `get` and `post` when running your test. Likewise, instead of mocking the `Config` facade, call the `Config::set` method in your tests.
diff --git a/notifications.md b/notifications.md
index 00139e45310..5cfbff0ee0a 100644
--- a/notifications.md
+++ b/notifications.md
@@ -1,7 +1,7 @@
# Notifications
- [Introduction](#introduction)
-- [Creating Notifications](#creating-notifications)
+- [Generating Notifications](#generating-notifications)
- [Sending Notifications](#sending-notifications)
- [Using The Notifiable Trait](#using-the-notifiable-trait)
- [Using The Notification Facade](#using-the-notification-facade)
@@ -48,18 +48,18 @@
## Introduction
-In addition to support for [sending email](/docs/{{version}}/mail), Laravel provides support for sending notifications across a variety of delivery channels, including mail, SMS (via [Vonage](https://www.vonage.com/communications-apis/), formerly known as Nexmo), and [Slack](https://slack.com). Notifications may also be stored in a database so they may be displayed in your web interface.
+In addition to support for [sending email](/docs/{{version}}/mail), Laravel provides support for sending notifications across a variety of delivery channels, including email, SMS (via [Vonage](https://www.vonage.com/communications-apis/), formerly known as Nexmo), and [Slack](https://slack.com). In addition, a variety of [community built notification channels](https://laravel-notification-channels.com/about/#suggesting-a-new-channel) have been created to send notification over dozens of different channels! Notifications may also be stored in a database so they may be displayed in your web interface.
Typically, notifications should be short, informational messages that notify users of something that occurred in your application. For example, if you are writing a billing application, you might send an "Invoice Paid" notification to your users via the email and SMS channels.
-
-## Creating Notifications
+
+## Generating Notifications
-In Laravel, each notification is represented by a single class (typically stored in the `app/Notifications` directory). Don't worry if you don't see this directory in your application, it will be created for you when you run the `make:notification` Artisan command:
+In Laravel, each notification is represented by a single class that is typically stored in the `app/Notifications` directory. Don't worry if you don't see this directory in your application - it will be created for you when you run the `make:notification` Artisan command:
php artisan make:notification InvoicePaid
-This command will place a fresh notification class in your `app/Notifications` directory. Each notification class contains a `via` method and a variable number of message building methods (such as `toMail` or `toDatabase`) that convert the notification to a message optimized for that particular channel.
+This command will place a fresh notification class in your `app/Notifications` directory. Each notification class contains a `via` method and a variable number of message building methods, such as `toMail` or `toDatabase`, that convert the notification to a message tailored for that particular channel.
## Sending Notifications
@@ -67,7 +67,7 @@ This command will place a fresh notification class in your `app/Notifications` d
### Using The Notifiable Trait
-Notifications may be sent in two ways: using the `notify` method of the `Notifiable` trait or using the `Notification` [facade](/docs/{{version}}/facades). First, let's explore using the trait:
+Notifications may be sent in two ways: using the `notify` method of the `Notifiable` trait or using the `Notification` [facade](/docs/{{version}}/facades). The `Notifiable` trait is included on your application's `App\Models\User` model by default:
notify(new InvoicePaid($invoice));
-> {tip} Remember, you may use the `Illuminate\Notifications\Notifiable` trait on any of your models. You are not limited to only including it on your `User` model.
+> {tip} Remember, you may use the `Notifiable` trait on any of your models. You are not limited to only including it on your `User` model.
### Using The Notification Facade
-Alternatively, you may send notifications via the `Notification` [facade](/docs/{{version}}/facades). This is useful primarily when you need to send a notification to multiple notifiable entities such as a collection of users. To send notifications using the facade, pass all of the notifiable entities and the notification instance to the `send` method:
+Alternatively, you may send notifications via the `Notification` [facade](/docs/{{version}}/facades). This approach is useful when you need to send a notification to multiple notifiable entities such as a collection of users. To send notifications using the facade, pass all of the notifiable entities and the notification instance to the `send` method:
+
+ use Illuminate\Support\Facades\Notification;
Notification::send($users, new InvoicePaid($invoice));
@@ -121,7 +123,7 @@ The `via` method receives a `$notifiable` instance, which will be an instance of
> {note} Before queueing notifications you should configure your queue and [start a worker](/docs/{{version}}/queues).
-Sending notifications can take time, especially if the channel needs an external API call to deliver the notification. To speed up your application's response time, let your notification be queued by adding the `ShouldQueue` interface and `Queueable` trait to your class. The interface and trait are already imported for all notifications generated using `make:notification`, so you may immediately add them to your notification class:
+Sending notifications can take time, especially if the channel needs to make an external API call to deliver the notification. To speed up your application's response time, let your notification be queued by adding the `ShouldQueue` interface and `Queueable` trait to your class. The interface and trait are already imported for all notifications generated using the `make:notification` command, so you may immediately add them to your notification class:
+#### Customizing The Notification Queue Connection
+
+By default, queued notifications will be queued using your application's default queue connection. If you would like to specify a different connection that should be used for a particular notification, you may define a `$connection` property on the notification class:
+
+ /**
+ * The name of the queue connection to use when queueing the notification.
+ *
+ * @var string
+ */
+ public $connection = 'redis';
+
#### Customizing Notification Channel Queues
@@ -178,7 +192,7 @@ If you would like to specify a specific queue that should be used for each notif
### On-Demand Notifications
-Sometimes you may need to send a notification to someone who is not stored as a "user" of your application. Using the `Notification::route` facade method, you may specify ad-hoc notification routing information before sending the notification:
+Sometimes you may need to send a notification to someone who is not stored as a "user" of your application. Using the `Notification` facade's `route` method, you may specify ad-hoc notification routing information before sending the notification:
Notification::route('mail', 'taylor@example.com')
->route('nexmo', '5555555555')
@@ -191,7 +205,9 @@ Sometimes you may need to send a notification to someone who is not stored as a
### Formatting Mail Messages
-If a notification supports being sent as an email, you should define a `toMail` method on the notification class. This method will receive a `$notifiable` entity and should return an `Illuminate\Notifications\Messages\MailMessage` instance. Mail messages may contain lines of text as well as a "call to action". Let's take a look at an example `toMail` method:
+If a notification supports being sent as an email, you should define a `toMail` method on the notification class. This method will receive a `$notifiable` entity and should return an `Illuminate\Notifications\Messages\MailMessage` instance.
+
+The `MailMessage` class contains a few simple methods to help you build transactional email messages. Mail messages may contain lines of text as well as a "call to action". Let's take a look at an example `toMail` method:
/**
* Get the mail representation of the notification.
@@ -212,14 +228,14 @@ If a notification supports being sent as an email, you should define a `toMail`
> {tip} Note we are using `$this->invoice->id` in our `toMail` method. You may pass any data your notification needs to generate its message into the notification's constructor.
-In this example, we register a greeting, a line of text, a call to action, and then another line of text. These methods provided by the `MailMessage` object make it simple and fast to format small transactional emails. The mail channel will then translate the message components into a nice, responsive HTML email template with a plain-text counterpart. Here is an example of an email generated by the `mail` channel:
+In this example, we register a greeting, a line of text, a call to action, and then another line of text. These methods provided by the `MailMessage` object make it simple and fast to format small transactional emails. The mail channel will then translate the message components into a beautiful, responsive HTML email template with a plain-text counterpart. Here is an example of an email generated by the `mail` channel:
-> {tip} When sending mail notifications, be sure to set the `name` value in your `config/app.php` configuration file. This value will be used in the header and footer of your mail notification messages.
+> {tip} When sending mail notifications, be sure to set the `name` configuration option in your `config/app.php` configuration file. This value will be used in the header and footer of your mail notification messages.
-
-#### Other Notification Formatting Options
+
+#### Other Mail Notification Formatting Options
Instead of defining the "lines" of text in the notification class, you may use the `view` method to specify a custom template that should be used to render the notification email:
@@ -236,7 +252,7 @@ Instead of defining the "lines" of text in the notification class, you may use t
);
}
-You may specify a plain-text view for the mail message by passing the view name as the second element of an array that is given to the `view` method of the `MailMessage`:
+You may specify a plain-text view for the mail message by passing the view name as the second element of an array that is given to the `view` method:
/**
* Get the mail representation of the notification.
@@ -254,7 +270,7 @@ You may specify a plain-text view for the mail message by passing the view name
In addition, you may return a full [mailable object](/docs/{{version}}/mail) from the `toMail` method:
- use App\Mail\InvoicePaid as Mailable;
+ use App\Mail\InvoicePaid as InvoicePaidMailable;
/**
* Get the mail representation of the notification.
@@ -264,13 +280,14 @@ In addition, you may return a full [mailable object](/docs/{{version}}/mail) fro
*/
public function toMail($notifiable)
{
- return (new Mailable($this->invoice))->to($notifiable->email);
+ return (new InvoicePaidMailable($this->invoice))
+ ->to($notifiable->email);
}
#### Error Messages
-Some notifications inform users of errors, such as a failed invoice payment. You may indicate that a mail message is regarding an error by calling the `error` method when building your message. When using the `error` method on a mail message, the call to action button will be red instead of blue:
+Some notifications inform users of errors, such as a failed invoice payment. You may indicate that a mail message is regarding an error by calling the `error` method when building your message. When using the `error` method on a mail message, the call to action button will be red instead of black:
/**
* Get the mail representation of the notification.
@@ -300,14 +317,14 @@ By default, the email's sender / from address is defined in the `config/mail.php
public function toMail($notifiable)
{
return (new MailMessage)
- ->from('test@example.com', 'Example')
+ ->from('barrett@example.com', 'Barrett Blair')
->line('...');
}
### Customizing The Recipient
-When sending notifications via the `mail` channel, the notification system will automatically look for an `email` property on your notifiable entity. You may customize which email address is used to deliver the notification by defining a `routeNotificationForMail` method on the entity:
+When sending notifications via the `mail` channel, the notification system will automatically look for an `email` property on your notifiable entity. You may customize which email address is used to deliver the notification by defining a `routeNotificationForMail` method on the notifiable entity:
email_address;
- // Return name and email address...
+ // Return email address and name...
return [$this->email_address => $this->name];
}
}
@@ -339,7 +356,7 @@ When sending notifications via the `mail` channel, the notification system will
### Customizing The Subject
-By default, the email's subject is the class name of the notification formatted to "title case". So, if your notification class is named `InvoicePaid`, the email's subject will be `Invoice Paid`. If you would like to specify an explicit subject for the message, you may call the `subject` method when building your message:
+By default, the email's subject is the class name of the notification formatted to "Title Case". So, if your notification class is named `InvoicePaid`, the email's subject will be `Invoice Paid`. If you would like to specify an different subject for the message, you may call the `subject` method when building your message:
/**
* Get the mail representation of the notification.
@@ -357,7 +374,7 @@ By default, the email's subject is the class name of the notification formatted
### Customizing The Mailer
-By default, the email notification will be sent using the default driver defined in the `config/mail.php` configuration file. However, you may specify a different mailer at runtime by calling the `mailer` method when building your message:
+By default, the email notification will be sent using the default mailer defined in the `config/mail.php` configuration file. However, you may specify a different mailer at runtime by calling the `mailer` method when building your message:
/**
* Get the mail representation of the notification.
@@ -382,7 +399,7 @@ You can modify the HTML and plain-text template used by mail notifications by pu
### Attachments
-To add attachments to an email notification, use the `attach` method while building your message. The `attach` method accepts the full (absolute) path to the file as its first argument:
+To add attachments to an email notification, use the `attach` method while building your message. The `attach` method accepts the absolute path to the file as its first argument:
/**
* Get the mail representation of the notification.
@@ -415,12 +432,27 @@ When attaching files to a message, you may also specify the display name and / o
]);
}
-> {tip} Unlike attaching files in mailable objects, you may not attach a file directly from the storage disk using `attachFromStorage`. You should rather use the `attach` method with an absolute path to the file on the storage disk. Alternatively, you could return a [mailable](/docs/{{version}}/mail#generating-mailables) from the `toMail` method.
+Unlike attaching files in mailable objects, you may not attach a file directly from a storage disk using `attachFromStorage`. You should rather use the `attach` method with an absolute path to the file on the storage disk. Alternatively, you could return a [mailable](/docs/{{version}}/mail#generating-mailables) from the `toMail` method:
+
+ use App\Mail\InvoicePaid as InvoicePaidMailable;
+
+ /**
+ * Get the mail representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return Mailable
+ */
+ public function toMail($notifiable)
+ {
+ return (new InvoicePaidMailable($this->invoice))
+ ->to($notifiable->email)
+ ->attachFromStorage('/path/to/file');
+ }
#### Raw Data Attachments
-The `attachData` method may be used to attach a raw string of bytes as an attachment:
+The `attachData` method may be used to attach a raw string of bytes as an attachment. When calling the `attachData` method, you should provide the filename that should be assigned to the attachment:
/**
* Get the mail representation of the notification.
@@ -440,12 +472,15 @@ The `attachData` method may be used to attach a raw string of bytes as an attach
### Previewing Mail Notifications
-When designing a mail notification template, it is convenient to quickly preview the rendered mail message in your browser like a typical Blade template. For this reason, Laravel allows you to return any mail message generated by a mail notification directly from a route Closure or controller. When a `MailMessage` is returned, it will be rendered and displayed in the browser, allowing you to quickly preview its design without needing to send it to an actual email address:
+When designing a mail notification template, it is convenient to quickly preview the rendered mail message in your browser like a typical Blade template. For this reason, Laravel allows you to return any mail message generated by a mail notification directly from a route closure or controller. When a `MailMessage` is returned, it will be rendered and displayed in the browser, allowing you to quickly preview its design without needing to send it to an actual email address:
- Route::get('mail', function () {
- $invoice = App\Invoice::find(1);
+ use App\Invoice;
+ use App\Notifications\InvoicePaid;
+
+ Route::get('/notification', function () {
+ $invoice = Invoice::find(1);
- return (new App\Notifications\InvoicePaid($invoice))
+ return (new InvoicePaid($invoice))
->toMail($invoice->user);
});
@@ -461,7 +496,7 @@ To generate a notification with a corresponding Markdown template, you may use t
php artisan make:notification InvoicePaid --markdown=mail.invoice.paid
-Like all other mail notifications, notifications that use Markdown templates should define a `toMail` method on their notification class. However, instead of using the `line` and `action` methods to construct the notification, use the `markdown` method to specify the name of the Markdown template that should be used:
+Like all other mail notifications, notifications that use Markdown templates should define a `toMail` method on their notification class. However, instead of using the `line` and `action` methods to construct the notification, use the `markdown` method to specify the name of the Markdown template that should be used. An array of data you wish to make available to the template may be passed as the method's second argument:
/**
* Get the mail representation of the notification.
@@ -499,7 +534,7 @@ Markdown mail notifications use a combination of Blade components and Markdown s
#### Button Component
-The button component renders a centered button link. The component accepts two arguments, a `url` and an optional `color`. Supported colors are `blue`, `green`, and `red`. You may add as many button components to a notification as you wish:
+The button component renders a centered button link. The component accepts two arguments, a `url` and an optional `color`. Supported colors are `primary`, `green`, and `red`. You may add as many button components to a notification as you wish:
@component('mail::button', ['url' => $url, 'color' => 'green'])
View Invoice
@@ -564,9 +599,9 @@ To customize the theme for an individual notification, you may call the `theme`
### Prerequisites
-The `database` notification channel stores the notification information in a database table. This table will contain information such as the notification type as well as custom JSON data that describes the notification.
+The `database` notification channel stores the notification information in a database table. This table will contain information such as the notification type as well as a JSON data structure that describes the notification.
-You can query the table to display the notifications in your application's user interface. But, before you can do that, you will need to create a database table to hold your notifications. You may use the `notifications:table` command to generate a migration with the proper table schema:
+You can query the table to display the notifications in your application's user interface. But, before you can do that, you will need to create a database table to hold your notifications. You may use the `notifications:table` command to generate a [migration](/docs/{{version}}/migrations) with the proper table schema:
php artisan notifications:table
@@ -594,12 +629,12 @@ If a notification supports being stored in a database table, you should define a
#### `toDatabase` Vs. `toArray`
-The `toArray` method is also used by the `broadcast` channel to determine which data to broadcast to your JavaScript client. If you would like to have two different array representations for the `database` and `broadcast` channels, you should define a `toDatabase` method instead of a `toArray` method.
+The `toArray` method is also used by the `broadcast` channel to determine which data to broadcast to your JavaScript powered frontend. If you would like to have two different array representations for the `database` and `broadcast` channels, you should define a `toDatabase` method instead of a `toArray` method.
### Accessing The Notifications
-Once notifications are stored in the database, you need a convenient way to access them from your notifiable entities. The `Illuminate\Notifications\Notifiable` trait, which is included on Laravel's default `App\Models\User` model, includes a `notifications` Eloquent relationship that returns the notifications for the entity. To fetch notifications, you may access this method like any other Eloquent relationship. By default, notifications will be sorted by the `created_at` timestamp:
+Once notifications are stored in the database, you need a convenient way to access them from your notifiable entities. The `Illuminate\Notifications\Notifiable` trait, which is included on Laravel's default `App\Models\User` model, includes a `notifications` [Eloquent relationship](/docs/{{version}}/eloquent-relationships) that returns the notifications for the entity. To fetch notifications, you may access this method like any other Eloquent relationship. By default, notifications will be sorted by the `created_at` timestamp with the most recent notifications at the beginning of the collection:
$user = App\Models\User::find(1);
@@ -607,7 +642,7 @@ Once notifications are stored in the database, you need a convenient way to acce
echo $notification->type;
}
-If you want to retrieve only the "unread" notifications, you may use the `unreadNotifications` relationship. Again, these notifications will be sorted by the `created_at` timestamp:
+If you want to retrieve only the "unread" notifications, you may use the `unreadNotifications` relationship. Again, these notifications will be sorted by the `created_at` timestamp with the most recent notifications at the beginning of the collection:
$user = App\Models\User::find(1);
@@ -615,7 +650,7 @@ If you want to retrieve only the "unread" notifications, you may use the `unread
echo $notification->type;
}
-> {tip} To access your notifications from your JavaScript client, you should define a notification controller for your application which returns the notifications for a notifiable entity, such as the current user. You may then make an HTTP request to that controller's URI from your JavaScript client.
+> {tip} To access your notifications from your JavaScript client, you should define a notification controller for your application which returns the notifications for a notifiable entity, such as the current user. You may then make an HTTP request to that controller's URL from your JavaScript client.
### Marking Notifications As Read
@@ -648,12 +683,12 @@ You may `delete` the notifications to remove them from the table entirely:
### Prerequisites
-Before broadcasting notifications, you should configure and be familiar with Laravel's [event broadcasting](/docs/{{version}}/broadcasting) services. Event broadcasting provides a way to react to server-side fired Laravel events from your JavaScript client.
+Before broadcasting notifications, you should configure and be familiar with Laravel's [event broadcasting](/docs/{{version}}/broadcasting) services. Event broadcasting provides a way to react to server-side Laravel events from your JavaScript powered frontend.
### Formatting Broadcast Notifications
-The `broadcast` channel broadcasts notifications using Laravel's [event broadcasting](/docs/{{version}}/broadcasting) services, allowing your JavaScript client to catch notifications in realtime. If a notification supports broadcasting, you can define a `toBroadcast` method on the notification class. This method will receive a `$notifiable` entity and should return a `BroadcastMessage` instance. If the `toBroadcast` method does not exist, the `toArray` method will be used to gather the data that should be broadcast. The returned data will be encoded as JSON and broadcast to your JavaScript client. Let's take a look at an example `toBroadcast` method:
+The `broadcast` channel broadcasts notifications using Laravel's [event broadcasting](/docs/{{version}}/broadcasting) services, allowing your JavaScript powered frontend to catch notifications in realtime. If a notification supports broadcasting, you can define a `toBroadcast` method on the notification class. This method will receive a `$notifiable` entity and should return a `BroadcastMessage` instance. If the `toBroadcast` method does not exist, the `toArray` method will be used to gather the data that should be broadcast. The returned data will be encoded as JSON and broadcast to your JavaScript powered frontend. Let's take a look at an example `toBroadcast` method:
use Illuminate\Notifications\Messages\BroadcastMessage;
@@ -683,7 +718,7 @@ All broadcast notifications are queued for broadcasting. If you would like to co
#### Customizing The Notification Type
-In addition to the data you specify, all broadcast notifications also have a `type` field containing the full class name of the notification. If you would like to customize the notification `type` that is provided to your JavaScript client, you may define a `broadcastType` method on the notification class:
+In addition to the data you specify, all broadcast notifications also have a `type` field containing the full class name of the notification. If you would like to customize the notification `type`, you may define a `broadcastType` method on the notification class:
use Illuminate\Notifications\Messages\BroadcastMessage;
@@ -700,7 +735,7 @@ In addition to the data you specify, all broadcast notifications also have a `ty
### Listening For Notifications
-Notifications will broadcast on a private channel formatted using a `{notifiable}.{id}` convention. So, if you are sending a notification to an `App\Models\User` instance with an ID of `1`, the notification will be broadcast on the `App.Models.User.1` private channel. When using [Laravel Echo](/docs/{{version}}/broadcasting), you may easily listen for notifications on a channel using the `notification` helper method:
+Notifications will broadcast on a private channel formatted using a `{notifiable}.{id}` convention. So, if you are sending a notification to an `App\Models\User` instance with an ID of `1`, the notification will be broadcast on the `App.Models.User.1` private channel. When using [Laravel Echo](/docs/{{version}}/broadcasting), you may easily listen for notifications on a channel using the `notification` method:
Echo.private('App.Models.User.' + userId)
.notification((notification) => {
@@ -710,7 +745,7 @@ Notifications will broadcast on a private channel formatted using a `{notifiable
#### Customizing The Notification Channel
-If you would like to customize which channels a notifiable entity receives its broadcast notifications on, you may define a `receivesBroadcastNotificationsOn` method on the notifiable entity:
+If you would like to customize which channel that an entity's broadcast notifications are broadcast on, you may define a `receivesBroadcastNotificationsOn` method on the notifiable entity:
### Prerequisites
-Sending SMS notifications in Laravel is powered by [Nexmo](https://www.nexmo.com/). Before you can send notifications via Nexmo, you need to install the `laravel/nexmo-notification-channel` Composer package:
+Sending SMS notifications in Laravel is powered by [Vonage](https://www.vonage.com/) (formerly known as Nexmo). Before you can send notifications via Vonage, you need to install the `laravel/nexmo-notification-channel` and `nexmo/laravel` Composer packages
- composer require laravel/nexmo-notification-channel
+ composer require laravel/nexmo-notification-channel nexmo/laravel
-This will also install the [`nexmo/laravel`](https://github.com/Nexmo/nexmo-laravel) package. This package includes [its own configuration file](https://github.com/Nexmo/nexmo-laravel/blob/master/config/nexmo.php). You can use the `NEXMO_KEY` and `NEXMO_SECRET` environment variables to set your Nexmo public and secret key.
+The `nexmo/laravel` package includes [its own configuration file](https://github.com/Nexmo/nexmo-laravel/blob/master/config/nexmo.php). However, you are not required to export this configuration file to your own application. You can simply use the `NEXMO_KEY` and `NEXMO_SECRET` environment variables to set your Vonage public and secret key.
-Next, you will need to add a configuration option to your `config/services.php` configuration file. You may copy the example configuration below to get started:
+Next, you will need to add a `nexmo` configuration entry to your `config/services.php` configuration file. You may copy the example configuration below to get started:
'nexmo' => [
'sms_from' => '15556666666',
],
-The `sms_from` option is the phone number that your SMS messages will be sent from. You should generate a phone number for your application in the Nexmo control panel.
+The `sms_from` option is the phone number that your SMS messages will be sent from. You should generate a phone number for your application in the Vonage control panel.
### Formatting SMS Notifications
@@ -761,10 +796,10 @@ The `sms_from` option is the phone number that your SMS messages will be sent fr
If a notification supports being sent as an SMS, you should define a `toNexmo` method on the notification class. This method will receive a `$notifiable` entity and should return an `Illuminate\Notifications\Messages\NexmoMessage` instance:
/**
- * Get the Nexmo / SMS representation of the notification.
+ * Get the Vonage / SMS representation of the notification.
*
* @param mixed $notifiable
- * @return NexmoMessage
+ * @return \Illuminate\Notifications\Messages\NexmoMessage
*/
public function toNexmo($notifiable)
{
@@ -772,13 +807,31 @@ If a notification supports being sent as an SMS, you should define a `toNexmo` m
->content('Your SMS message content');
}
+
+#### Unicode Content
+
+If your SMS message will contain unicode characters, you should call the `unicode` method when constructing the `NexmoMessage` instance:
+
+ /**
+ * Get the Vonage / SMS representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return \Illuminate\Notifications\Messages\NexmoMessage
+ */
+ public function toNexmo($notifiable)
+ {
+ return (new NexmoMessage)
+ ->content('Your unicode message')
+ ->unicode();
+ }
+
### Formatting Shortcode Notifications
-Laravel also supports sending shortcode notifications, which are pre-defined message templates in your Nexmo account. You may specify the type of notification (`alert`, `2fa`, or `marketing`), as well as the custom values that will populate the template:
+Laravel also supports sending shortcode notifications, which are pre-defined message templates in your Vonage account. To send a shortcode SMS notification, you should define a `toShortcode` method on your notification class. From within this method, you may return an array specifying the type of notification (`alert`, `2fa`, or `marketing`) as well as the custom values that will populate the template:
/**
- * Get the Nexmo / Shortcode representation of the notification.
+ * Get the Vonage / Shortcode representation of the notification.
*
* @param mixed $notifiable
* @return array
@@ -795,31 +848,13 @@ Laravel also supports sending shortcode notifications, which are pre-defined mes
> {tip} Like [routing SMS Notifications](#routing-sms-notifications), you should implement the `routeNotificationForShortcode` method on your notifiable model.
-
-#### Unicode Content
-
-If your SMS message will contain unicode characters, you should call the `unicode` method when constructing the `NexmoMessage` instance:
-
- /**
- * Get the Nexmo / SMS representation of the notification.
- *
- * @param mixed $notifiable
- * @return NexmoMessage
- */
- public function toNexmo($notifiable)
- {
- return (new NexmoMessage)
- ->content('Your unicode message')
- ->unicode();
- }
-
### Customizing The "From" Number
-If you would like to send some notifications from a phone number that is different from the phone number specified in your `config/services.php` file, you may use the `from` method on a `NexmoMessage` instance:
+If you would like to send some notifications from a phone number that is different from the phone number specified in your `config/services.php` file, you may call the `from` method on a `NexmoMessage` instance:
/**
- * Get the Nexmo / SMS representation of the notification.
+ * Get the Vonage / SMS representation of the notification.
*
* @param mixed $notifiable
* @return NexmoMessage
@@ -834,7 +869,7 @@ If you would like to send some notifications from a phone number that is differe
### Routing SMS Notifications
-To route Nexmo notifications to the proper phone number, define a `routeNotificationForNexmo` method on your notifiable entity:
+To route Vonage notifications to the proper phone number, define a `routeNotificationForNexmo` method on your notifiable entity:
### Prerequisites
-Before you can send notifications via Slack, you must install the notification channel via Composer:
+Before you can send notifications via Slack, you must install the Slack notification channel via Composer:
composer require laravel/slack-notification-channel
@@ -880,7 +915,7 @@ If a notification supports being sent as a Slack message, you should define a `t
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
- * @return SlackMessage
+ * @return \Illuminate\Notifications\Message\SlackMessage
*/
public function toSlack($notifiable)
{
@@ -897,23 +932,23 @@ You may use the `from` and `to` methods to customize the sender and recipient. T
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
- * @return SlackMessage
+ * @return \Illuminate\Notifications\Messages\SlackMessage
*/
public function toSlack($notifiable)
{
return (new SlackMessage)
->from('Ghost', ':ghost:')
- ->to('#other')
- ->content('This will be sent to #other');
+ ->to('#bots')
+ ->content('This will be sent to #bots');
}
-You may also use an image as your logo instead of an emoji:
+You may also use an image as your from "logo" instead of an emoji:
/**
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
- * @return SlackMessage
+ * @return \Illuminate\Notifications\Messages\SlackMessage
*/
public function toSlack($notifiable)
{
@@ -932,7 +967,7 @@ You may also add "attachments" to Slack messages. Attachments provide richer for
* Get the Slack representation of the notification.
*
* @param mixed $notifiable
- * @return SlackMessage
+ * @return \Illuminate\Notifications\Messages\SlackMessage
*/
public function toSlack($notifiable)
{
@@ -1001,7 +1036,7 @@ If some of your attachment fields contain Markdown, you may use the `markdown` m
### Routing Slack Notifications
-To route Slack notifications to the proper location, define a `routeNotificationForSlack` method on your notifiable entity. This should return the webhook URL to which the notification should be delivered. Webhook URLs may be generated by adding an "Incoming Webhook" service to your Slack team:
+To route Slack notifications to the proper Slack team and channel, define a `routeNotificationForSlack` method on your notifiable entity. This should return the webhook URL to which the notification should be delivered. Webhook URLs may be generated by adding an "Incoming Webhook" service to your Slack team:
## Localizing Notifications
-Laravel allows you to send notifications in a locale other than the current language, and will even remember this locale if the notification is queued.
+Laravel allows you to send notifications in a locale other than the HTTP request's current locale, and will even remember this locale if the notification is queued.
-To accomplish this, the `Illuminate\Notifications\Notification` class offers a `locale` method to set the desired language. The application will change into this locale when the notification is being formatted and then revert back to the previous locale when formatting is complete:
+To accomplish this, the `Illuminate\Notifications\Notification` class offers a `locale` method to set the desired language. The application will change into this locale when the notification is being evaluated and then revert back to the previous locale when evaluation is complete:
$user->notify((new InvoicePaid($invoice))->locale('es'));
Localization of multiple notifiable entries may also be achieved via the `Notification` facade:
- Notification::locale('es')->send($users, new InvoicePaid($invoice));
+ Notification::locale('es')->send(
+ $users, new InvoicePaid($invoice)
+ );
### User Preferred Locales
@@ -1066,7 +1103,7 @@ Once you have implemented the interface, Laravel will automatically use the pref
## Notification Events
-When a notification is sent, the `Illuminate\Notifications\Events\NotificationSent` event is fired by the notification system. This contains the "notifiable" entity and the notification instance itself. You may register listeners for this event in your `EventServiceProvider`:
+When a notification is sent, the `Illuminate\Notifications\Events\NotificationSent` [event](/docs/{{version}}/events) is fired by the notification system. This contains the "notifiable" entity and the notification instance itself. You may register listeners for this event in your `EventServiceProvider`:
/**
* The event listener mappings for the application.
@@ -1086,7 +1123,7 @@ Within an event listener, you may access the `notifiable`, `notification`, and `
/**
* Handle the event.
*
- * @param NotificationSent $event
+ * @param \Illuminate\Notifications\Events\NotificationSent $event
* @return void
*/
public function handle(NotificationSent $event)
@@ -1100,7 +1137,9 @@ Within an event listener, you may access the `notifiable`, `notification`, and `
## Custom Channels
-Laravel ships with a handful of notification channels, but you may want to write your own drivers to deliver notifications via other channels. Laravel makes it simple. To get started, define a class that contains a `send` method. The method should receive two arguments: a `$notifiable` and a `$notification`:
+Laravel ships with a handful of notification channels, but you may want to write your own drivers to deliver notifications via other channels. Laravel makes it simple. To get started, define a class that contains a `send` method. The method should receive two arguments: a `$notifiable` and a `$notification`.
+
+Within the `send` method, you may call methods on the notification to retrieve a message object understood by your channel and then send the notification to the `$notifiable` instance however you wish:
## Introduction
-Packages are the primary way of adding functionality to Laravel. Packages might be anything from a great way to work with dates like [Carbon](https://github.com/briannesbitt/Carbon), or an entire BDD testing framework like [Behat](https://github.com/Behat/Behat).
+Packages are the primary way of adding functionality to Laravel. Packages might be anything from a great way to work with dates like [Carbon](https://github.com/briannesbitt/Carbon) or a package that allows you to associate files with Eloquent models like Spatie's [Laravel Media Library](https://github.com/spatie/laravel-medialibrary).
-There are different types of packages. Some packages are stand-alone, meaning they work with any PHP framework. Carbon and Behat are examples of stand-alone packages. Any of these packages may be used with Laravel by requesting them in your `composer.json` file.
+There are different types of packages. Some packages are stand-alone, meaning they work with any PHP framework. Carbon and PHPUnit are examples of stand-alone packages. Any of these packages may be used with Laravel by requiring them in your `composer.json` file.
On the other hand, other packages are specifically intended for use with Laravel. These packages may have routes, controllers, views, and configuration specifically intended to enhance a Laravel application. This guide primarily covers the development of those packages that are Laravel specific.
### A Note On Facades
-When writing a Laravel application, it generally does not matter if you use contracts or facades since both provide essentially equal levels of testability. However, when writing packages, your package will not typically have access to all of Laravel's testing helpers. If you would like to be able to write your package tests as if they existed inside a typical Laravel application, you may use the [Orchestral Testbench](https://github.com/orchestral/testbench) package.
+When writing a Laravel application, it generally does not matter if you use contracts or facades since both provide essentially equal levels of testability. However, when writing packages, your package will not typically have access to all of Laravel's testing helpers. If you would like to be able to write your package tests as if the package were installed inside a typical Laravel application, you may use the [Orchestral Testbench](https://github.com/orchestral/testbench) package.
## Package Discovery
@@ -73,7 +73,7 @@ You may disable package discovery for all packages using the `*` character insid
## Service Providers
-[Service providers](/docs/{{version}}/providers) are the connection points between your package and Laravel. A service provider is responsible for binding things into Laravel's [service container](/docs/{{version}}/container) and informing Laravel where to load package resources such as views, configuration, and localization files.
+[Service providers](/docs/{{version}}/providers) are the connection point between your package and Laravel. A service provider is responsible for binding things into Laravel's [service container](/docs/{{version}}/container) and informing Laravel where to load package resources such as views, configuration, and localization files.
A service provider extends the `Illuminate\Support\ServiceProvider` class and contains two methods: `register` and `boot`. The base `ServiceProvider` class is located in the `illuminate/support` Composer package, which you should add to your own package's dependencies. To learn more about the structure and purpose of service providers, check out [their documentation](/docs/{{version}}/providers).
@@ -83,17 +83,17 @@ A service provider extends the `Illuminate\Support\ServiceProvider` class and co
### Configuration
-Typically, you will need to publish your package's configuration file to the application's own `config` directory. This will allow users of your package to easily override your default configuration options. To allow your configuration files to be published, call the `publishes` method from the `boot` method of your service provider:
+Typically, you will need to publish your package's configuration file to the application's `config` directory. This will allow users of your package to easily override your default configuration options. To allow your configuration files to be published, call the `publishes` method from the `boot` method of your service provider:
/**
- * Bootstrap any application services.
+ * Bootstrap any package services.
*
* @return void
*/
public function boot()
{
$this->publishes([
- __DIR__.'/path/to/config/courier.php' => config_path('courier.php'),
+ __DIR__.'/../config/courier.php' => config_path('courier.php'),
]);
}
@@ -101,12 +101,14 @@ Now, when users of your package execute Laravel's `vendor:publish` command, your
$value = config('courier.option');
-> {note} You should not define Closures in your configuration files. They can not be serialized correctly when users execute the `config:cache` Artisan command.
+> {note} You should not define closures in your configuration files. They can not be serialized correctly when users execute the `config:cache` Artisan command.
#### Default Package Configuration
-You may also merge your own package configuration file with the application's published copy. This will allow your users to define only the options they actually want to override in the published copy of the configuration. To merge the configurations, use the `mergeConfigFrom` method within your service provider's `register` method:
+You may also merge your own package configuration file with the application's published copy. This will allow your users to define only the options they actually want to override in the published copy of the configuration file. To merge the configuration file values, use the `mergeConfigFrom` method within your service provider's `register` method.
+
+The `mergeConfigFrom` method accepts the path to your package's configuration file as its first argument and the name of the application's copy of the configuration file as its second argument:
/**
* Register any application services.
@@ -116,7 +118,7 @@ You may also merge your own package configuration file with the application's pu
public function register()
{
$this->mergeConfigFrom(
- __DIR__.'/path/to/config/courier.php', 'courier'
+ __DIR__.'/../config/courier.php', 'courier'
);
}
@@ -128,13 +130,13 @@ You may also merge your own package configuration file with the application's pu
If your package contains routes, you may load them using the `loadRoutesFrom` method. This method will automatically determine if the application's routes are cached and will not load your routes file if the routes have already been cached:
/**
- * Bootstrap any application services.
+ * Bootstrap any package services.
*
* @return void
*/
public function boot()
{
- $this->loadRoutesFrom(__DIR__.'/routes.php');
+ $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}
@@ -143,16 +145,16 @@ If your package contains routes, you may load them using the `loadRoutesFrom` me
If your package contains [database migrations](/docs/{{version}}/migrations), you may use the `loadMigrationsFrom` method to inform Laravel how to load them. The `loadMigrationsFrom` method accepts the path to your package's migrations as its only argument:
/**
- * Bootstrap any application services.
+ * Bootstrap any package services.
*
* @return void
*/
public function boot()
{
- $this->loadMigrationsFrom(__DIR__.'/path/to/migrations');
+ $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}
-Once your package's migrations have been registered, they will automatically be run when the `php artisan migrate` command is executed. You do not need to export them to the application's main `database/migrations` directory.
+Once your package's migrations have been registered, they will automatically be run when the `php artisan migrate` command is executed. You do not need to export them to the application's `database/migrations` directory.
### Translations
@@ -160,13 +162,13 @@ Once your package's migrations have been registered, they will automatically be
If your package contains [translation files](/docs/{{version}}/localization), you may use the `loadTranslationsFrom` method to inform Laravel how to load them. For example, if your package is named `courier`, you should add the following to your service provider's `boot` method:
/**
- * Bootstrap any application services.
+ * Bootstrap any package services.
*
* @return void
*/
public function boot()
{
- $this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
+ $this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'courier');
}
Package translations are referenced using the `package::file.line` syntax convention. So, you may load the `courier` package's `welcome` line from the `messages` file like so:
@@ -179,16 +181,16 @@ Package translations are referenced using the `package::file.line` syntax conven
If you would like to publish your package's translations to the application's `resources/lang/vendor` directory, you may use the service provider's `publishes` method. The `publishes` method accepts an array of package paths and their desired publish locations. For example, to publish the translation files for the `courier` package, you may do the following:
/**
- * Bootstrap any application services.
+ * Bootstrap any package services.
*
* @return void
*/
public function boot()
{
- $this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
+ $this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'courier');
$this->publishes([
- __DIR__.'/path/to/translations' => resource_path('lang/vendor/courier'),
+ __DIR__.'/../resources/lang' => resource_path('lang/vendor/courier'),
]);
}
@@ -200,25 +202,25 @@ Now, when users of your package execute Laravel's `vendor:publish` Artisan comma
To register your package's [views](/docs/{{version}}/views) with Laravel, you need to tell Laravel where the views are located. You may do this using the service provider's `loadViewsFrom` method. The `loadViewsFrom` method accepts two arguments: the path to your view templates and your package's name. For example, if your package's name is `courier`, you would add the following to your service provider's `boot` method:
/**
- * Bootstrap any application services.
+ * Bootstrap any package services.
*
* @return void
*/
public function boot()
{
- $this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
+ $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
}
Package views are referenced using the `package::view` syntax convention. So, once your view path is registered in a service provider, you may load the `admin` view from the `courier` package like so:
- Route::get('admin', function () {
- return view('courier::admin');
+ Route::get('/dashboard', function () {
+ return view('courier::dashboard');
});
#### Overriding Package Views
-When you use the `loadViewsFrom` method, Laravel actually registers two locations for your views: the application's `resources/views/vendor` directory and the directory you specify. So, using the `courier` example, Laravel will first check if a custom version of the view has been provided by the developer in `resources/views/vendor/courier`. Then, if the view has not been customized, Laravel will search the package view directory you specified in your call to `loadViewsFrom`. This makes it easy for package users to customize / override your package's views.
+When you use the `loadViewsFrom` method, Laravel actually registers two locations for your views: the application's `resources/views/vendor` directory and the directory you specify. So, using the `courier` package as an example, Laravel will first check if a custom version of the view has been placed in the `resources/views/vendor/courier` directory by the developer. Then, if the view has not been customized, Laravel will search the package view directory you specified in your call to `loadViewsFrom`. This makes it easy for package users to customize / override your package's views.
#### Publishing Views
@@ -226,16 +228,16 @@ When you use the `loadViewsFrom` method, Laravel actually registers two location
If you would like to make your views available for publishing to the application's `resources/views/vendor` directory, you may use the service provider's `publishes` method. The `publishes` method accepts an array of package view paths and their desired publish locations:
/**
- * Bootstrap any application services.
+ * Bootstrap the package services.
*
* @return void
*/
public function boot()
{
- $this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
+ $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
$this->publishes([
- __DIR__.'/path/to/views' => resource_path('views/vendor/courier'),
+ __DIR__.'/../resources/views' => resource_path('views/vendor/courier'),
]);
}
@@ -244,10 +246,13 @@ Now, when users of your package execute Laravel's `vendor:publish` Artisan comma
### View Components
-If your package contains [view components](/docs/{{version}}/blade#components), you may use the `loadViewComponentsAs` method to inform Laravel how to load them. The `loadViewComponentsAs` method accepts two arguments: the tag prefix for your view components and an array of your view components class. For example, if your package's prefix is `courier` and you have `Alert` and `Button` view components, you would add the following to your service provider's `boot` method:
+If your package contains [view components](/docs/{{version}}/blade#components), you may use the `loadViewComponentsAs` method to inform Laravel how to load them. The `loadViewComponentsAs` method accepts two arguments: the tag prefix for your view components and an array of your view component class names. For example, if your package's prefix is `courier` and you have `Alert` and `Button` view components, you would add the following to your service provider's `boot` method:
+
+ use Courier\Components\Alert;
+ use Courier\Components\Button;
/**
- * Bootstrap any application services.
+ * Bootstrap any package services.
*
* @return void
*/
@@ -277,8 +282,11 @@ If your package contains anonymous components, they must be placed within a `com
To register your package's Artisan commands with Laravel, you may use the `commands` method. This method expects an array of command class names. Once the commands have been registered, you may execute them using the [Artisan CLI](/docs/{{version}}/artisan):
+ use Courier\Console\Commands\InstallCommand;
+ use Courier\Console\Commands\NetworkCommand;
+
/**
- * Bootstrap the application services.
+ * Bootstrap any package services.
*
* @return void
*/
@@ -286,8 +294,8 @@ To register your package's Artisan commands with Laravel, you may use the `comma
{
if ($this->app->runningInConsole()) {
$this->commands([
- FooCommand::class,
- BarCommand::class,
+ InstallCommand::class,
+ NetworkCommand::class,
]);
}
}
@@ -295,31 +303,31 @@ To register your package's Artisan commands with Laravel, you may use the `comma
## Public Assets
-Your package may have assets such as JavaScript, CSS, and images. To publish these assets to the application's `public` directory, use the service provider's `publishes` method. In this example, we will also add a `public` asset group tag, which may be used to publish groups of related assets:
+Your package may have assets such as JavaScript, CSS, and images. To publish these assets to the application's `public` directory, use the service provider's `publishes` method. In this example, we will also add a `public` asset group tag, which may be used to easily publish groups of related assets:
/**
- * Bootstrap any application services.
+ * Bootstrap any package services.
*
* @return void
*/
public function boot()
{
$this->publishes([
- __DIR__.'/path/to/assets' => public_path('vendor/courier'),
+ __DIR__.'/../public' => public_path('vendor/courier'),
], 'public');
}
-Now, when your package's users execute the `vendor:publish` command, your assets will be copied to the specified publish location. Since you will typically need to overwrite the assets every time the package is updated, you may use the `--force` flag:
+Now, when your package's users execute the `vendor:publish` command, your assets will be copied to the specified publish location. Since users will typically need to overwrite the assets every time the package is updated, you may use the `--force` flag:
php artisan vendor:publish --tag=public --force
## Publishing File Groups
-You may want to publish groups of package assets and resources separately. For instance, you might want to allow your users to publish your package's configuration files without being forced to publish your package's assets. You may do this by "tagging" them when calling the `publishes` method from a package's service provider. For example, let's use tags to define two publish groups in the `boot` method of a package service provider:
+You may want to publish groups of package assets and resources separately. For instance, you might want to allow your users to publish your package's configuration files without being forced to publish your package's assets. You may do this by "tagging" them when calling the `publishes` method from a package's service provider. For example, let's use tags to define two publish groups (`config` and `migrations`) in the `boot` method of a package's service provider:
/**
- * Bootstrap any application services.
+ * Bootstrap any package services.
*
* @return void
*/
diff --git a/pagination.md b/pagination.md
index 594304d3fc3..d15c4ebd684 100644
--- a/pagination.md
+++ b/pagination.md
@@ -5,7 +5,9 @@
- [Paginating Query Builder Results](#paginating-query-builder-results)
- [Paginating Eloquent Results](#paginating-eloquent-results)
- [Manually Creating A Paginator](#manually-creating-a-paginator)
+ - [Customizing Pagination URLs](#customizing-pagination-urls)
- [Displaying Pagination Results](#displaying-pagination-results)
+ - [Adjusting The Pagination Link Window](#adjusting-the-pagination-link-window)
- [Converting Results To JSON](#converting-results-to-json)
- [Customizing The Pagination View](#customizing-the-pagination-view)
- [Using Bootstrap](#using-bootstrap)
@@ -14,7 +16,9 @@
## Introduction
-In other frameworks, pagination can be very painful. Laravel's paginator is integrated with the [query builder](/docs/{{version}}/queries) and [Eloquent ORM](/docs/{{version}}/eloquent) and provides convenient, easy-to-use pagination of database results out of the box. By default, the HTML generated by the paginator is compatible with the [Tailwind CSS framework](https://tailwindcss.com/); however, Bootstrap views are also available.
+In other frameworks, pagination can be very painful. We hope Laravel's approach to pagination will be a breath of fresh air. Laravel's paginator is integrated with the [query builder](/docs/{{version}}/queries) and [Eloquent ORM](/docs/{{version}}/eloquent) and provides convenient, easy-to-use pagination of database records with zero configuration.
+
+By default, the HTML generated by the paginator is compatible with the [Tailwind CSS framework](https://tailwindcss.com/); however, Bootstrap pagination support is also available.
## Basic Usage
@@ -22,7 +26,7 @@ In other frameworks, pagination can be very painful. Laravel's paginator is inte
### Paginating Query Builder Results
-There are several ways to paginate items. The simplest is by using the `paginate` method on the [query builder](/docs/{{version}}/queries) or an [Eloquent query](/docs/{{version}}/eloquent). The `paginate` method automatically takes care of setting the proper limit and offset based on the current page being viewed by the user. By default, the current page is detected by the value of the `page` query string argument on the HTTP request. This value is automatically detected by Laravel, and is also automatically inserted into links generated by the paginator.
+There are several ways to paginate items. The simplest is by using the `paginate` method on the [query builder](/docs/{{version}}/queries) or an [Eloquent query](/docs/{{version}}/eloquent). The `paginate` method automatically takes care of setting the query's "limit" and "offset" based on the current page being viewed by the user. By default, the current page is detected by the value of the `page` query string argument on the HTTP request. This value is automatically detected by Laravel, and is also automatically inserted into links generated by the paginator.
In this example, the only argument passed to the `paginate` method 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:
@@ -38,33 +42,35 @@ In this example, the only argument passed to the `paginate` method is the number
/**
* Show all of the users for the application.
*
- * @return Response
+ * @return \Illuminate\Http\Response
*/
public function index()
{
- $users = DB::table('users')->paginate(15);
-
- return view('user.index', ['users' => $users]);
+ return view('user.index', [
+ 'users' => DB::table('users')->paginate(15)
+ ]);
}
}
-> {note} Currently, pagination operations that use a `groupBy` statement cannot be executed efficiently by Laravel. 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.
-
#### "Simple Pagination"
-If you only need to display simple "Next" and "Previous" links in your pagination view, you may use the `simplePaginate` method to perform a more efficient query. This is very useful for large datasets when you do not need to display a link for each page number when rendering your view:
+The `paginate` method counts the total number of records matched by the query before retrieving the records from the database. This is done so that the paginator knows how many pages of records there are in total. However, if you do not plan to show the total number of pages in your application's UI then the record count query is unnecessary.
+
+Therefore, if you only need to display simple "Next" and "Previous" links in your application's UI, you may use the `simplePaginate` method to perform a single, efficient query:
$users = DB::table('users')->simplePaginate(15);
### Paginating Eloquent Results
-You may also paginate [Eloquent](/docs/{{version}}/eloquent) 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:
+You may also paginate [Eloquent](/docs/{{version}}/eloquent) queries. In this example, we will paginate the `App\Models\User` model and indicate that we plan to display 15 records per page. As you can see, the syntax is nearly identical to paginating query builder results:
- $users = App\Models\User::paginate(15);
+ use App\Models\User;
-You may call `paginate` after setting other constraints on the query, such as `where` clauses:
+ $users = User::paginate(15);
+
+Of course, you may call the `paginate` method after setting other constraints on the query, such as `where` clauses:
$users = User::where('votes', '>', 100)->paginate(15);
@@ -75,74 +81,91 @@ You may also use the `simplePaginate` method when paginating Eloquent models:
### Manually Creating A Paginator
-Sometimes you may wish to create a pagination instance manually, passing it an array of items. You may do so by creating either an `Illuminate\Pagination\Paginator` or `Illuminate\Pagination\LengthAwarePaginator` instance, depending on your needs.
+Sometimes you may wish to create a pagination instance manually, passing it an array of items that you already have in memory. You may do so by creating either an `Illuminate\Pagination\Paginator` or `Illuminate\Pagination\LengthAwarePaginator` instance, depending on your needs.
-The `Paginator` class does not need to know the total number of items in the result set; however, because of this, the class does not have methods for retrieving the index of the last page. The `LengthAwarePaginator` accepts almost the same arguments as the `Paginator`; however, it does require a count of the total number of items in the result set.
+The `Paginator` class does not need to know the total number of items in the result set; however, because of this, the class does not have methods for retrieving the index of the last page. The `LengthAwarePaginator` accepts almost the same arguments as the `Paginator`; however, it requires a count of the total number of items in the result set.
-In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder and Eloquent, while the `LengthAwarePaginator` corresponds to the `paginate` method.
+In other words, the `Paginator` corresponds to the `simplePaginate` method on the query builder, while the `LengthAwarePaginator` corresponds to the `paginate` method.
> {note} When manually creating a paginator instance, you should manually "slice" the array of results you pass to the paginator. If you're unsure how to do this, check out the [array_slice](https://secure.php.net/manual/en/function.array-slice.php) PHP function.
-
-## Displaying Pagination Results
+
+### Customizing Pagination URLs
-When calling the `paginate` method, you will receive an instance of `Illuminate\Pagination\LengthAwarePaginator`. When calling the `simplePaginate` method, you will receive an instance of `Illuminate\Pagination\Paginator`. These objects provide several methods that describe the result set. In addition to these helpers methods, the paginator instances are iterators and may be looped as an array. So, once you have retrieved the results, you may display the results and render the page links using [Blade](/docs/{{version}}/blade):
+By default, links generated by the paginator will match the current request's URI. However, the paginator's `withPath` 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/admin/users?page=N`, you should pass `/admin/users` to the `withPath` method:
-
+ use App\Models\User;
- {{ $users->links() }}
+ Route::get('/users', function () {
+ $users = User::paginate(15);
-The `links` 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. Remember, the HTML generated by the `links` method is compatible with the [Tailwind CSS framework](https://tailwindcss.com).
+ $users->withPath('/admin/users');
+
+ //
+ });
-
-#### Customizing The Paginator URI
+
+#### Appending Query String Values
-The `withPath` 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 `withPath` method:
+You may append 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`:
+
+ use App\Models\User;
- Route::get('users', function () {
- $users = App\Models\User::paginate(15);
+ Route::get('/users', function () {
+ $users = User::paginate(15);
- $users->withPath('custom/url');
+ $users->appends(['sort' => 'votes']);
//
});
-
-#### Appending To Pagination Links
+You may use the `withQueryString` method if you would like to append all of the current request's query string values to the pagination links:
-You may append 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 = User::paginate(15)->withQueryString();
+
+
+#### Appending Hash Fragments
- {{ $users->appends(['sort' => 'votes'])->links() }}
+If you need to append a "hash fragment" to URLs generated by the paginator, you may use the `fragment` method. For example, to append `#users` to the end of each pagination link, you should invoke the `fragment` method like so:
-If you wish to append all current query string values to the pagination links you may use the `withQueryString` method:
+ $users = User::paginate(15)->fragment('users');
- {{ $users->withQueryString()->links() }}
+
+## Displaying Pagination Results
+
+When calling the `paginate` method, you will receive an instance of `Illuminate\Pagination\LengthAwarePaginator`. When calling the `simplePaginate` method, you will receive an instance of `Illuminate\Pagination\Paginator`. These objects provide several methods that describe the result set. In addition to these helpers methods, the paginator instances are iterators and may be looped as an array. So, once you have retrieved the results, you may display the results and render the page links using [Blade](/docs/{{version}}/blade):
-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:
+```html
+
- {{ $users->fragment('foo')->links() }}
+{{ $users->links() }}
+```
+
+The `links` 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. Remember, the HTML generated by the `links` method is compatible with the [Tailwind CSS framework](https://tailwindcss.com).
-#### Adjusting The Pagination Link Window
+### Adjusting The Pagination Link Window
-You may control how many additional links are displayed on each side of the paginator's URL "window". By default, three links are displayed on each side of the primary paginator links. However, you may control this number using the `onEachSide` method:
+When the paginator displays pagination links, the current page number is displayed as well as links for the three pages before and after the current page. If needed, you may control how many additional links are displayed on each side of the current page using the `onEachSide` method:
{{ $users->onEachSide(5)->links() }}
### Converting Results To JSON
-The Laravel paginator result classes implement the `Illuminate\Contracts\Support\Jsonable` Interface 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 returning it from a route or controller action:
+The Laravel paginator classes implement the `Illuminate\Contracts\Support\Jsonable` Interface 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 returning it from a route or controller action:
- Route::get('users', function () {
- return App\Models\User::paginate();
+ use App\Models\User;
+
+ 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:
+The JSON from the paginator will include meta information such as `total`, `current_page`, `last_page`, and more. The result records are 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:
{
"total": 50,
@@ -158,10 +181,10 @@ The JSON from the paginator will include meta information such as `total`, `curr
"to": 15,
"data":[
{
- // Result Object
+ // Record...
},
{
- // Result Object
+ // Record...
}
]
}
@@ -169,37 +192,56 @@ The JSON from the paginator will include meta information such as `total`, `curr
## Customizing The Pagination View
-By default, the views rendered to display the pagination links are compatible with the Tailwind CSS framework. However, if you are not using Tailwind, you are free to define your own views to render these links. When calling the `links` method on a paginator instance, pass the view name as the first argument to the method:
+By default, the views rendered to display the pagination links are compatible with the [Tailwind CSS](https://tailwindcss.com) framework. However, if you are not using Tailwind, you are free to define your own views to render these links. When calling the `links` method on a paginator instance, you may pass the view name as the first argument to the method:
{{ $paginator->links('view.name') }}
- // Passing data to the view...
+ // Passing additional data to the view...
{{ $paginator->links('view.name', ['foo' => 'bar']) }}
However, the easiest way to customize the pagination views is by exporting them to your `resources/views/vendor` directory using the `vendor:publish` command:
php artisan vendor:publish --tag=laravel-pagination
-This command will place the views in the `resources/views/vendor/pagination` directory. The `tailwind.blade.php` file within this directory corresponds to the default pagination view. You may edit this file to modify the pagination HTML.
+This command will place the views in your application's `resources/views/vendor/pagination` directory. The `tailwind.blade.php` file within this directory corresponds to the default pagination view. You may edit this file to modify the pagination HTML.
+
+If you would like to designate a different file as the default pagination view, you may invoke the paginator's `defaultView` and `defaultSimpleView` methods within the `boot` method of your `App\Providers\AppServiceProvider` class:
-If you would like to designate a different file as the default pagination view, you may use the paginator's `defaultView` and `defaultSimpleView` methods within your `AppServiceProvider`:
+
### Using Bootstrap
-Laravel includes pagination views built using [Bootstrap CSS](https://getbootstrap.com/). To use these views instead of the default Tailwind views, you may call the paginator's `useBootstrap` method within your `AppServiceProvider`:
+Laravel includes pagination views built using [Bootstrap CSS](https://getbootstrap.com/). To use these views instead of the default Tailwind views, you may call the paginator's `useBootstrap` method within the `boot` method of your `App\Providers\AppServiceProvider` class:
use Illuminate\Pagination\Paginator;
+ /**
+ * Bootstrap any application services.
+ *
+ * @return void
+ */
public function boot()
{
Paginator::useBootstrap();
diff --git a/passport.md b/passport.md
index 8834f0123c6..bc95a0d9393 100644
--- a/passport.md
+++ b/passport.md
@@ -1,10 +1,11 @@
# Laravel Passport
- [Introduction](#introduction)
-- [Upgrading Passport](#upgrading)
+ - [Passport Or Sanctum?](#passport-or-sanctum)
- [Installation](#installation)
- [Deploying Passport](#deploying-passport)
- [Migration Customization](#migration-customization)
+ - [Upgrading Passport](#upgrading-passport)
- [Configuration](#configuration)
- [Client Secret Hashing](#client-secret-hashing)
- [Token Lifetimes](#token-lifetimes)
@@ -45,14 +46,16 @@
## Introduction
-Laravel already makes it easy to perform authentication via traditional login forms, but what about APIs? APIs typically use tokens to authenticate users and do not maintain session state between requests. Laravel makes API authentication a breeze using Laravel Passport, which provides a full OAuth2 server implementation for your Laravel application in a matter of minutes. Passport is built on top of the [League OAuth2 server](https://github.com/thephpleague/oauth2-server) that is maintained by Andy Millington and Simon Hamp.
+Laravel Passport provides a full OAuth2 server implementation for your Laravel application in a matter of minutes. Passport is built on top of the [League OAuth2 server](https://github.com/thephpleague/oauth2-server) that is maintained by Andy Millington and Simon Hamp.
> {note} This documentation assumes you are already familiar with OAuth2. If you do not know anything about OAuth2, consider familiarizing yourself with the general [terminology](https://oauth2.thephpleague.com/terminology/) and features of OAuth2 before continuing.
-
-## Upgrading Passport
+
+### Passport Or Sanctum?
-When upgrading to a new major version of Passport, it's important that you carefully review [the upgrade guide](https://github.com/laravel/passport/blob/master/UPGRADE.md).
+Before getting started, you may wish to determine if your application would be better served by Laravel Passport or [Laravel Sanctum](/docs/{{version}}/sanctum). If your application absolutely needs to support OAuth2, then you should use Laravel Passport.
+
+However, if you are attempting to authenticate a single-page application, mobile application, or issue API tokens, you should use [Laravel Sanctum](/docs/{{version}}/sanctum). Laravel Sanctum does not support OAuth2; however, it provides a much simpler API authentication development experience.
## Installation
@@ -61,11 +64,11 @@ To get started, install Passport via the Composer package manager:
composer require laravel/passport
-The Passport service provider registers its own database migration directory with the framework, so you should migrate your database after installing the package. The Passport migrations will create the tables your application needs to store clients and access tokens:
+Passport's [service provider](/docs/{{version}}/providers) registers its own database migration directory, so you should migrate your database after installing the package. The Passport migrations will create the tables your application needs to store OAuth2 clients and access tokens:
php artisan migrate
-Next, you should run the `passport:install` command. This command will create the encryption keys needed to generate secure access tokens. In addition, the command will create "personal access" and "password grant" clients which will be used to generate access tokens:
+Next, you should execute the `passport:install` Artisan command. This command will create the encryption keys needed to generate secure access tokens. In addition, the command will create "personal access" and "password grant" clients which will be used to generate access tokens:
php artisan passport:install
@@ -87,7 +90,7 @@ After running the `passport:install` command, add the `Laravel\Passport\HasApiTo
use HasApiTokens, HasFactory, Notifiable;
}
-Next, you should call the `Passport::routes` method within the `boot` method of your `AuthServiceProvider`. This method will register the routes necessary to issue access tokens and revoke access tokens, clients, and personal access tokens:
+Next, you should call the `Passport::routes` method within the `boot` method of your `App\Providers\AuthServiceProvider`. This method will register the routes necessary to issue access tokens and revoke access tokens, clients, and personal access tokens:
[
'web' => [
@@ -138,18 +141,18 @@ Finally, in your `config/auth.php` configuration file, you should set the `drive
#### Client UUIDs
-You may run the `passport:install` command with the `--uuids` option present. This flag will instruct Passport that you would like to use UUIDs instead of auto-incrementing integers as the Passport `Client` model's primary key values. After running the `passport:install` command with the `--uuids` option, you will be given additional instructions regarding disabling Passport's default migrations:
+You may also run the `passport:install` command with the `--uuids` option present. This option will instruct Passport that you would like to use UUIDs instead of auto-incrementing integers as the Passport `Client` model's primary key values. After running the `passport:install` command with the `--uuids` option, you will be given additional instructions regarding disabling Passport's default migrations:
php artisan passport:install --uuids
### Deploying Passport
-When deploying Passport to your production servers for the first time, you will likely need to run the `passport:keys` command. This command generates the encryption keys Passport needs in order to generate access token. The generated keys are not typically kept in source control:
+When deploying Passport to your application's servers for the first time, you will likely need to run the `passport:keys` command. This command generates the encryption keys Passport needs in order to generate access tokens. The generated keys are not typically kept in source control:
php artisan passport:keys
-If necessary, you may define the path where Passport's keys should be loaded from. You may use the `Passport::loadKeysFrom` method to accomplish this:
+If necessary, you may define the path where Passport's keys should be loaded from. You may use the `Passport::loadKeysFrom` method to accomplish this. Typically, this method should be called from the `boot` method of your application's `App\Providers\AuthServiceProvider` class:
/**
* Register any authentication / authorization services.
@@ -162,23 +165,39 @@ If necessary, you may define the path where Passport's keys should be loaded fro
Passport::routes();
- Passport::loadKeysFrom('/secret-keys/oauth');
+ Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');
}
-Additionally, you may publish Passport's configuration file using `php artisan vendor:publish --tag=passport-config`, which will then provide the option to load the encryption keys from your environment variables:
+
+#### Loading Keys From The Environment
+
+Alternatively, you may publish Passport's configuration file using the `vendor:publish` Artisan command:
+
+ php artisan vendor:publish --tag=passport-config
+
+After the configuration file has been published, you may load your application's encryption keys by defining them as environment variables:
- PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
-
- -----END RSA PRIVATE KEY-----"
+```bash
+PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
+
+-----END RSA PRIVATE KEY-----"
- PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
-
- -----END PUBLIC KEY-----"
+PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
+
+-----END PUBLIC KEY-----"
+```
### Migration Customization
-If you are not going to use Passport's default migrations, you should call the `Passport::ignoreMigrations` method in the `register` method of your `AppServiceProvider`. You may export the default migrations using `php artisan vendor:publish --tag=passport-migrations`.
+If you are not going to use Passport's default migrations, you should call the `Passport::ignoreMigrations` method in the `register` method of your `App\Providers\AppServiceProvider` class. You may export the default migrations using the `vendor:publish` Artisan command:
+
+ php artisan vendor:publish --tag=passport-migrations
+
+
+### Upgrading Passport
+
+When upgrading to a new major version of Passport, it's important that you carefully review [the upgrade guide](https://github.com/laravel/passport/blob/master/UPGRADE.md).
## Configuration
@@ -186,16 +205,18 @@ If you are not going to use Passport's default migrations, you should call the `
### Client Secret Hashing
-If you would like your client's secrets to be hashed when stored in your database, you should call the `Passport::hashClientSecrets` method in the `boot` method of your `AppServiceProvider`:
+If you would like your client's secrets to be hashed when stored in your database, you should call the `Passport::hashClientSecrets` method in the `boot` method of your `App\Providers\AuthServiceProvider` class:
+
+ use Laravel\Passport\Passport;
Passport::hashClientSecrets();
-Once enabled, all of your client secrets will only be shown one time when your client is created. Since the plain-text client secret value is never stored in the database, it is not possible to recover if lost.
+Once enabled, all of your client secrets will only be displayable to the user immediately after they are created. Since the plain-text client secret value is never stored in the database, it is not possible to recover the secret's value if it is lost.
### Token Lifetimes
-By default, Passport issues long-lived access tokens that expire after one year. If you would like to configure a longer / shorter token lifetime, you may use the `tokensExpireIn`, `refreshTokensExpireIn`, and `personalAccessTokensExpireIn` methods. These methods should be called from the `boot` method of your `AuthServiceProvider`:
+By default, Passport issues long-lived access tokens that expire after one year. If you would like to configure a longer / shorter token lifetime, you may use the `tokensExpireIn`, `refreshTokensExpireIn`, and `personalAccessTokensExpireIn` methods. These methods should be called from the `boot` method of your application's `App\Providers\AuthServiceProvider` class:
/**
* Register any authentication / authorization services.
@@ -209,18 +230,16 @@ By default, Passport issues long-lived access tokens that expire after one year.
Passport::routes();
Passport::tokensExpireIn(now()->addDays(15));
-
Passport::refreshTokensExpireIn(now()->addDays(30));
-
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}
-> {note} The `expires_at` columns on the Passport database tables are read-only and for display purposes only. When issuing tokens, Passport stores the expiration information within the signed and encrypted tokens. If you need to invalidate a token you should revoke it.
+> {note} The `expires_at` columns on Passport's database tables are read-only and for display purposes only. When issuing tokens, Passport stores the expiration information within the signed and encrypted tokens. If you need to invalidate a token you should [revoke it](#revoking-tokens).
### Overriding Default Models
-You are free to extend the models used internally by Passport:
+You are free to extend the models used internally by Passport by defining your own model and extending the corresponding Passport model:
use Laravel\Passport\Client as PassportClient;
@@ -229,7 +248,7 @@ You are free to extend the models used internally by Passport:
// ...
}
-Then, you may instruct Passport to use your custom models via the `Passport` class:
+After defining your model, you may instruct Passport to use your custom model via the `Laravel\Passport\Passport` class. Typically, you should inform Passport about your custom models in the `boot` method of your application's `App\Providers\AuthServiceProvider` class:
use App\Models\Passport\AuthCode;
use App\Models\Passport\Client;
@@ -256,7 +275,7 @@ Then, you may instruct Passport to use your custom models via the `Passport` cla
## Issuing Access Tokens
-Using OAuth2 with authorization codes is how most developers are familiar with OAuth2. When using authorization codes, a client application will redirect a user to your server where they will either approve or deny the request to issue an access token to the client.
+Using OAuth2 via authorization codes is how most developers are familiar with OAuth2. When using authorization codes, a client application will redirect a user to your server where they will either approve or deny the request to issue an access token to the client.
### Managing Clients
@@ -272,16 +291,16 @@ The simplest way to create a client is using the `passport:client` Artisan comma
**Redirect URLs**
-If you would like to allow multiple redirect URLs for your client, you may specify them using a comma-delimited list when prompted for the URL by the `passport:client` command:
+If you would like to allow multiple redirect URLs for your client, you may specify them using a comma-delimited list when prompted for the URL by the `passport:client` command. Any URLs which contain commas should be URL encoded:
- http://example.com/callback,http://examplefoo.com/callback
-
-> {note} Any URL which contains commas must be encoded.
+```bash
+http://example.com/callback,http://examplefoo.com/callback
+```
#### JSON API
-Since your users will not be able to utilize the `client` command, Passport provides a JSON API that you may use to create clients. This saves you the trouble of having to manually code controllers for creating, updating, and deleting clients.
+Since your application's users will not be able to utilize the `client` command, Passport provides a JSON API that you may use to create clients. This saves you the trouble of having to manually code controllers for creating, updating, and deleting clients.
However, you will need to pair Passport's JSON API with your own frontend to provide a dashboard for your users to manage their clients. Below, we'll review all of the API endpoints for managing clients. For convenience, we'll use [Axios](https://github.com/axios/axios) to demonstrate making HTTP requests to the endpoints.
@@ -353,18 +372,21 @@ This route is used to delete clients:
Once a client has been created, developers may use their client ID and secret to request an authorization code and access token from your application. First, the consuming application should make a redirect request to your application's `/oauth/authorize` route like so:
+ use Illuminate\Http\Request;
+ use Illuminate\Support\Str;
+
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
$query = http_build_query([
'client_id' => 'client-id',
- 'redirect_uri' => 'http://example.com/callback',
+ 'redirect_uri' => 'http://third-party-app.com/callback',
'response_type' => 'code',
'scope' => '',
'state' => $state,
]);
- return redirect('http://your-app.com/oauth/authorize?'.$query);
+ return redirect('http://passport-app.com/oauth/authorize?'.$query);
});
> {tip} Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route.
@@ -374,7 +396,7 @@ Once a client has been created, developers may use their client ID and secret to
When receiving authorization requests, Passport will automatically display a template to the user allowing them to approve or deny the authorization request. If they approve the request, they will be redirected back to the `redirect_uri` that was specified by the consuming application. The `redirect_uri` must match the `redirect` URL that was specified when the client was created.
-If you would like to customize the authorization approval screen, you may publish Passport's views using the `vendor:publish` Artisan command. The published views will be placed in `resources/views/vendor/passport`:
+If you would like to customize the authorization approval screen, you may publish Passport's views using the `vendor:publish` Artisan command. The published views will be placed in the `resources/views/vendor/passport` directory:
php artisan vendor:publish --tag=passport-views
@@ -402,7 +424,10 @@ Sometimes you may wish to skip the authorization prompt, such as when authorizin
#### Converting Authorization Codes To Access Tokens
-If the user approves the authorization request, they will be redirected back to the consuming application. The consumer should first verify the `state` parameter against the value that was stored prior to the redirect. If the state parameter matches the consumer should issue a `POST` request to your application to request an access token. The request should include the authorization code that was issued by your application when the user approved the authorization request. In this example, we'll use the Guzzle HTTP library to make the `POST` request:
+If the user approves the authorization request, they will be redirected back to the consuming application. The consumer should first verify the `state` parameter against the value that was stored prior to the redirect. If the state parameter matches then the consumer should issue a `POST` request to your application to request an access token. The request should include the authorization code that was issued by your application when the user approved the authorization request:
+
+ use Illuminate\Http\Request;
+ use Illuminate\Support\Facades\Http;
Route::get('/callback', function (Request $request) {
$state = $request->session()->pull('state');
@@ -412,24 +437,20 @@ If the user approves the authorization request, they will be redirected back to
InvalidArgumentException::class
);
- $http = new GuzzleHttp\Client;
-
- $response = $http->post('http://your-app.com/oauth/token', [
- 'form_params' => [
- 'grant_type' => 'authorization_code',
- 'client_id' => 'client-id',
- 'client_secret' => 'client-secret',
- 'redirect_uri' => 'http://example.com/callback',
- 'code' => $request->code,
- ],
+ $response = Http::asForm()->post('http://passport-app.com/oauth/token', [
+ 'grant_type' => 'authorization_code',
+ 'client_id' => 'client-id',
+ 'client_secret' => 'client-secret',
+ 'redirect_uri' => 'http://third-party-app.com/callback',
+ 'code' => $request->code,
]);
- return json_decode((string) $response->getBody(), true);
+ return $response->json();
});
This `/oauth/token` route will return a JSON response containing `access_token`, `refresh_token`, and `expires_in` attributes. The `expires_in` attribute contains the number of seconds until the access token expires.
-> {tip} Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by the `Passport::routes` method. There is no need to manually define this route. By default, this route is throttled using the settings of the `ThrottleRequests` middleware.
+> {tip} Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by the `Passport::routes` method. There is no need to manually define this route.
#### JSON API
@@ -456,31 +477,32 @@ This route may be used to revoke authorized access tokens and their related refr
### Refreshing Tokens
-If your application issues short-lived access tokens, users will need to refresh their access tokens via the refresh token that was provided to them when the access token was issued. In this example, we'll use the Guzzle HTTP library to refresh the token:
+If your application issues short-lived access tokens, users will need to refresh their access tokens via the refresh token that was provided to them when the access token was issued:
- $http = new GuzzleHttp\Client;
+ use Illuminate\Support\Facades\Http;
- $response = $http->post('http://your-app.com/oauth/token', [
- 'form_params' => [
- 'grant_type' => 'refresh_token',
- 'refresh_token' => 'the-refresh-token',
- 'client_id' => 'client-id',
- 'client_secret' => 'client-secret',
- 'scope' => '',
- ],
+ $response = Http::asForm()->post('http://passport-app.com/oauth/token', [
+ 'grant_type' => 'refresh_token',
+ 'refresh_token' => 'the-refresh-token',
+ 'client_id' => 'client-id',
+ 'client_secret' => 'client-secret',
+ 'scope' => '',
]);
- return json_decode((string) $response->getBody(), true);
+ return $response->json();
This `/oauth/token` route will return a JSON response containing `access_token`, `refresh_token`, and `expires_in` attributes. The `expires_in` attribute contains the number of seconds until the access token expires.
### Revoking Tokens
-You may revoke a token by using the `revokeAccessToken` method on the `TokenRepository`. You may revoke a token's refresh tokens using the `revokeRefreshTokensByAccessTokenId` method on the `RefreshTokenRepository`:
+You may revoke a token by using the `revokeAccessToken` method on the `Laravel\Passport\TokenRepository`. You may revoke a token's refresh tokens using the `revokeRefreshTokensByAccessTokenId` method on the `Laravel\Passport\RefreshTokenRepository`. These classes may be resolved using Laravel's [service container](/docs/{{version}}/container):
+
+ use Laravel\Passport\TokenRepository;
+ use Laravel\Passport\RefreshTokenRepository;
- $tokenRepository = app('Laravel\Passport\TokenRepository');
- $refreshTokenRepository = app('Laravel\Passport\RefreshTokenRepository');
+ $tokenRepository = app(TokenRepository::class);
+ $refreshTokenRepository = app(RefreshTokenRepository::class);
// Revoke an access token...
$tokenRepository->revokeAccessToken($tokenId);
@@ -491,7 +513,7 @@ You may revoke a token by using the `revokeAccessToken` method on the `TokenRepo
### Purging Tokens
-When tokens have been revoked or expired, you might want to purge them from the database. Passport ships with a command that can do this for you:
+When tokens have been revoked or expired, you might want to purge them from the database. Passport's included `passport:purge` Artisan command can do this for you:
# Purge revoked and expired tokens and auth codes...
php artisan passport:purge
@@ -502,7 +524,7 @@ When tokens have been revoked or expired, you might want to purge them from the
# Only purge expired tokens and auth codes...
php artisan passport:purge --expired
-You may also configure a [scheduled job](/docs/{{version}}/scheduling) in your console `Kernel` class to automatically prune your tokens on a schedule:
+You may also configure a [scheduled job](/docs/{{version}}/scheduling) in your application's `App\Console\Kernel` class to automatically prune your tokens on a schedule:
/**
* Define the application's command schedule.
@@ -523,7 +545,7 @@ The Authorization Code grant with "Proof Key for Code Exchange" (PKCE) is a secu
### Creating The Client
-Before your application can issue tokens via the authorization code grant with PKCE, you will need to create a PKCE-enabled client. You may do this using the `passport:client` command with the `--public` option:
+Before your application can issue tokens via the authorization code grant with PKCE, you will need to create a PKCE-enabled client. You may do this using the `passport:client` Artisan command with the `--public` option:
php artisan passport:client --public
@@ -535,7 +557,7 @@ Before your application can issue tokens via the authorization code grant with P
As this authorization grant does not provide a client secret, developers will need to generate a combination of a code verifier and a code challenge in order to request a token.
-The code verifier should be a random string of between 43 and 128 characters containing letters, numbers and `"-"`, `"."`, `"_"`, `"~"`, as defined in the [RFC 7636 specification](https://tools.ietf.org/html/rfc7636).
+The code verifier should be a random string of between 43 and 128 characters containing letters, numbers, and `"-"`, `"."`, `"_"`, `"~"` characters, as defined in the [RFC 7636 specification](https://tools.ietf.org/html/rfc7636).
The code challenge should be a Base64 encoded string with URL and filename-safe characters. The trailing `'='` characters should be removed and no line breaks, whitespace, or other additional characters should be present.
@@ -548,10 +570,15 @@ The code challenge should be a Base64 encoded string with URL and filename-safe
Once a client has been created, you may use the client ID and the generated code verifier and code challenge to request an authorization code and access token from your application. First, the consuming application should make a redirect request to your application's `/oauth/authorize` route:
+ use Illuminate\Http\Request;
+ use Illuminate\Support\Str;
+
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
- $request->session()->put('code_verifier', $code_verifier = Str::random(128));
+ $request->session()->put(
+ 'code_verifier', $code_verifier = Str::random(128)
+ );
$codeChallenge = strtr(rtrim(
base64_encode(hash('sha256', $code_verifier, true))
@@ -559,7 +586,7 @@ Once a client has been created, you may use the client ID and the generated code
$query = http_build_query([
'client_id' => 'client-id',
- 'redirect_uri' => 'http://example.com/callback',
+ 'redirect_uri' => 'http://third-party-app.com/callback',
'response_type' => 'code',
'scope' => '',
'state' => $state,
@@ -567,7 +594,7 @@ Once a client has been created, you may use the client ID and the generated code
'code_challenge_method' => 'S256',
]);
- return redirect('http://your-app.com/oauth/authorize?'.$query);
+ return redirect('http://passport-app.com/oauth/authorize?'.$query);
});
@@ -577,6 +604,9 @@ If the user approves the authorization request, they will be redirected back to
If the state parameter matches, the consumer should issue a `POST` request to your application to request an access token. The request should include the authorization code that was issued by your application when the user approved the authorization request along with the originally generated code verifier:
+ use Illuminate\Http\Request;
+ use Illuminate\Support\Facades\Http;
+
Route::get('/callback', function (Request $request) {
$state = $request->session()->pull('state');
@@ -587,28 +617,26 @@ If the state parameter matches, the consumer should issue a `POST` request to yo
InvalidArgumentException::class
);
- $response = (new GuzzleHttp\Client)->post('http://your-app.com/oauth/token', [
- 'form_params' => [
- 'grant_type' => 'authorization_code',
- 'client_id' => 'client-id',
- 'redirect_uri' => 'http://example.com/callback',
- 'code_verifier' => $codeVerifier,
- 'code' => $request->code,
- ],
+ $response = Http::asForm()->post('http://passport-app.com/oauth/token', [
+ 'grant_type' => 'authorization_code',
+ 'client_id' => 'client-id',
+ 'redirect_uri' => 'http://third-party-app.com/callback',
+ 'code_verifier' => $codeVerifier,
+ 'code' => $request->code,
]);
- return json_decode((string) $response->getBody(), true);
+ return $response->json();
});
## Password Grant Tokens
-The OAuth2 password grant allows your other first-party clients, such as a mobile application, to obtain an access token using an e-mail address / username and password. This allows you to issue access tokens securely to your first-party clients without requiring your users to go through the entire OAuth2 authorization code redirect flow.
+The OAuth2 password grant allows your other first-party clients, such as a mobile application, to obtain an access token using an email address / username and password. This allows you to issue access tokens securely to your first-party clients without requiring your users to go through the entire OAuth2 authorization code redirect flow.
### Creating A Password Grant Client
-Before your application can issue tokens via the password grant, you will need to create a password grant client. You may do this using the `passport:client` command with the `--password` option. If you have already run the `passport:install` command, you do not need to run this command:
+Before your application can issue tokens via the password grant, you will need to create a password grant client. You may do this using the `passport:client` Artisan command with the `--password` option. **If you have already run the `passport:install` command, you do not need to run this command:**
php artisan passport:client --password
@@ -617,20 +645,18 @@ Before your application can issue tokens via the password grant, you will need t
Once you have created a password grant client, you may request an access token by issuing a `POST` request to the `/oauth/token` route with the user's email address and password. Remember, this route is already registered by the `Passport::routes` method so there is no need to define it manually. If the request is successful, you will receive an `access_token` and `refresh_token` in the JSON response from the server:
- $http = new GuzzleHttp\Client;
+ use Illuminate\Support\Facades\Http;
- $response = $http->post('http://your-app.com/oauth/token', [
- 'form_params' => [
- 'grant_type' => 'password',
- 'client_id' => 'client-id',
- 'client_secret' => 'client-secret',
- 'username' => 'taylor@laravel.com',
- 'password' => 'my-password',
- 'scope' => '',
- ],
+ $response = Http::asForm()->post('http://passport-app.com/oauth/token', [
+ 'grant_type' => 'password',
+ 'client_id' => 'client-id',
+ 'client_secret' => 'client-secret',
+ 'username' => 'taylor@laravel.com',
+ 'password' => 'my-password',
+ 'scope' => '',
]);
- return json_decode((string) $response->getBody(), true);
+ return $response->json();
> {tip} Remember, access tokens are long-lived by default. However, you are free to [configure your maximum access token lifetime](#configuration) if needed.
@@ -639,26 +665,26 @@ Once you have created a password grant client, you may request an access token b
When using the password grant or client credentials grant, you may wish to authorize the token for all of the scopes supported by your application. You can do this by requesting the `*` scope. If you request the `*` scope, the `can` method on the token instance will always return `true`. This scope may only be assigned to a token that is issued using the `password` or `client_credentials` grant:
- $response = $http->post('http://your-app.com/oauth/token', [
- 'form_params' => [
- 'grant_type' => 'password',
- 'client_id' => 'client-id',
- 'client_secret' => 'client-secret',
- 'username' => 'taylor@laravel.com',
- 'password' => 'my-password',
- 'scope' => '*',
- ],
+ use Illuminate\Support\Facades\Http;
+
+ $response = Http::asForm()->post('http://passport-app.com/oauth/token', [
+ 'grant_type' => 'password',
+ 'client_id' => 'client-id',
+ 'client_secret' => 'client-secret',
+ 'username' => 'taylor@laravel.com',
+ 'password' => 'my-password',
+ 'scope' => '*',
]);
### Customizing The User Provider
-If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the password grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --password` command. The given provider name should match a valid provider defined in your `config/auth.php` configuration file. You can then [protect your route using middleware](#via-middleware) to ensure that only users from the guard's specified provider are authorized.
+If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the password grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --password` command. The given provider name should match a valid provider defined in your application's `config/auth.php` configuration file. You can then [protect your route using middleware](#via-middleware) to ensure that only users from the guard's specified provider are authorized.
### Customizing The Username Field
-When authenticating using the password grant, Passport will use the `email` attribute of your model as the "username". However, you may customize this behavior by defining a `findForPassport` method on your model:
+When authenticating using the password grant, Passport will use the `email` attribute of your authenticatable model as the "username". However, you may customize this behavior by defining a `findForPassport` method on your model:
## Implicit Grant Tokens
-The implicit grant is similar to the authorization code grant; however, the token is returned to the client without exchanging an authorization code. This grant is most commonly used for JavaScript or mobile applications where the client credentials can't be securely stored. To enable the grant, call the `enableImplicitGrant` method in your `AuthServiceProvider`:
+The implicit grant is similar to the authorization code grant; however, the token is returned to the client without exchanging an authorization code. This grant is most commonly used for JavaScript or mobile applications where the client credentials can't be securely stored. To enable the grant, call the `enableImplicitGrant` method in the `boot` method of your applications's `App\Providers\AuthServiceProvider` class:
/**
* Register any authentication / authorization services.
@@ -733,20 +759,22 @@ The implicit grant is similar to the authorization code grant; however, the toke
Passport::enableImplicitGrant();
}
-Once a grant has been enabled, developers may use their client ID to request an access token from your application. The consuming application should make a redirect request to your application's `/oauth/authorize` route like so:
+Once the grant has been enabled, developers may use their client ID to request an access token from your application. The consuming application should make a redirect request to your application's `/oauth/authorize` route like so:
+
+ use Illuminate\Http\Request;
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
$query = http_build_query([
'client_id' => 'client-id',
- 'redirect_uri' => 'http://example.com/callback',
+ 'redirect_uri' => 'http://third-party-app.com/callback',
'response_type' => 'token',
'scope' => '',
'state' => $state,
]);
- return redirect('http://your-app.com/oauth/authorize?'.$query);
+ return redirect('http://passport-app.com/oauth/authorize?'.$query);
});
> {tip} Remember, the `/oauth/authorize` route is already defined by the `Passport::routes` method. You do not need to manually define this route.
@@ -756,7 +784,7 @@ Once a grant has been enabled, developers may use their client ID to request an
The client credentials grant is suitable for machine-to-machine authentication. For example, you might use this grant in a scheduled job which is performing maintenance tasks over an API.
-Before your application can issue tokens via the client credentials grant, you will need to create a client credentials grant client. You may do this using the `--client` option of the `passport:client` command:
+Before your application can issue tokens via the client credentials grant, you will need to create a client credentials grant client. You may do this using the `--client` option of the `passport:client` Artisan command:
php artisan passport:client --client
@@ -774,7 +802,7 @@ Then, attach the middleware to a route:
...
})->middleware('client');
-To restrict access to the route to specific scopes you may provide a comma-delimited list of the required scopes when attaching the `client` middleware to the route:
+To restrict access to the route to specific scopes, you may provide a comma-delimited list of the required scopes when attaching the `client` middleware to the route:
Route::get('/orders', function (Request $request) {
...
@@ -785,18 +813,16 @@ To restrict access to the route to specific scopes you may provide a comma-delim
To retrieve a token using this grant type, make a request to the `oauth/token` endpoint:
- $guzzle = new GuzzleHttp\Client;
+ use Illuminate\Support\Facades\Http;
- $response = $guzzle->post('http://your-app.com/oauth/token', [
- 'form_params' => [
- 'grant_type' => 'client_credentials',
- 'client_id' => 'client-id',
- 'client_secret' => 'client-secret',
- 'scope' => 'your-scope',
- ],
+ $response = Http::asForm()->post('http://passport-app.com/oauth/token', [
+ 'grant_type' => 'client_credentials',
+ 'client_id' => 'client-id',
+ 'client_secret' => 'client-secret',
+ 'scope' => 'your-scope',
]);
- return json_decode((string) $response->getBody(), true)['access_token'];
+ return $response->json()['access_token'];
## Personal Access Tokens
@@ -806,21 +832,25 @@ Sometimes, your users may want to issue access tokens to themselves without goin
### Creating A Personal Access Client
-Before your application can issue personal access tokens, you will need to create a personal access client. You may do this using the `passport:client` command with the `--personal` option. If you have already run the `passport:install` command, you do not need to run this command:
+Before your application can issue personal access tokens, you will need to create a personal access client. You may do this by executing the `passport:client` Artisan command with the `--personal` option. If you have already run the `passport:install` command, you do not need to run this command:
php artisan passport:client --personal
After creating your personal access client, place the client's ID and plain-text secret value in your application's `.env` file:
- PASSPORT_PERSONAL_ACCESS_CLIENT_ID=client-id-value
- PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET=unhashed-client-secret-value
+```bash
+PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value"
+PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value"
+```
### Managing Personal Access Tokens
-Once you have created a personal access client, you may issue tokens for a given user using the `createToken` method on the `User` model instance. The `createToken` method accepts the name of the token as its first argument and an optional array of [scopes](#token-scopes) as its second argument:
+Once you have created a personal access client, you may issue tokens for a given user using the `createToken` method on the `App\Models\User` model instance. The `createToken` method accepts the name of the token as its first argument and an optional array of [scopes](#token-scopes) as its second argument:
+
+ use App\Models\User;
- $user = App\Models\User::find(1);
+ $user = User::find(1);
// Creating a token without scopes...
$token = $user->createToken('Token Name')->accessToken;
@@ -886,7 +916,7 @@ This route may be used to revoke personal access tokens:
### Via Middleware
-Passport includes an [authentication guard](/docs/{{version}}/authentication#adding-custom-guards) that will validate access tokens on incoming requests. Once you have configured the `api` guard to use the `passport` driver, you only need to specify the `auth:api` middleware on any routes that require a valid access token:
+Passport includes an [authentication guard](/docs/{{version}}/authentication#adding-custom-guards) that will validate access tokens on incoming requests. Once you have configured the `api` guard to use the `passport` driver, you only need to specify the `auth:api` middleware on any routes that should require a valid access token:
Route::get('/user', function () {
//
@@ -920,12 +950,14 @@ The following route will utilize the `api-customers` guard, which uses the `cust
When calling routes that are protected by Passport, your application's API consumers should specify their access token as a `Bearer` token in the `Authorization` header of their request. For example, when using the Guzzle HTTP library:
- $response = $client->request('GET', '/api/user', [
- 'headers' => [
- 'Accept' => 'application/json',
- 'Authorization' => 'Bearer '.$accessToken,
- ],
- ]);
+ use Illuminate\Support\Facades\Http;
+
+ $response = Http::withHeaders([
+ 'Accept' => 'application/json',
+ 'Authorization' => 'Bearer '.$accessToken,
+ ])->get('https://passport-app.com/api/user');
+
+ return $response->json();
## Token Scopes
@@ -935,22 +967,37 @@ Scopes allow your API clients to request a specific set of permissions when requ
### Defining Scopes
-You may define your API's scopes using the `Passport::tokensCan` method in the `boot` method of your `AuthServiceProvider`. The `tokensCan` method accepts an array of scope names and scope descriptions. The scope description may be anything you wish and will be displayed to users on the authorization approval screen:
+You may define your API's scopes using the `Passport::tokensCan` method in the `boot` method of your application's `App\Providers\AuthServiceProvider` class. The `tokensCan` method accepts an array of scope names and scope descriptions. The scope description may be anything you wish and will be displayed to users on the authorization approval screen:
- use Laravel\Passport\Passport;
+ /**
+ * Register any authentication / authorization services.
+ *
+ * @return void
+ */
+ public function boot()
+ {
+ $this->registerPolicies();
- Passport::tokensCan([
- 'place-orders' => 'Place orders',
- 'check-status' => 'Check order status',
- ]);
+ Passport::routes();
+
+ Passport::tokensCan([
+ 'place-orders' => 'Place orders',
+ 'check-status' => 'Check order status',
+ ]);
+ }
### Default Scope
-If a client does not request any specific scopes, you may configure your Passport server to attach a default scope to the token using the `setDefaultScope` method. Typically, you should call this method from the `boot` method of your `AuthServiceProvider`:
+If a client does not request any specific scopes, you may configure your Passport server to attach default scope(s) to the token using the `setDefaultScope` method. Typically, you should call this method from the `boot` method of your application's `App\Providers\AuthServiceProvider` class:
use Laravel\Passport\Passport;
+ Passport::tokensCan([
+ 'place-orders' => 'Place orders',
+ 'check-status' => 'Check order status',
+ ]);
+
Passport::setDefaultScope([
'check-status',
'place-orders',
@@ -972,13 +1019,13 @@ When requesting an access token using the authorization code grant, consumers sh
'scope' => 'place-orders check-status',
]);
- return redirect('http://your-app.com/oauth/authorize?'.$query);
+ return redirect('http://passport-app.com/oauth/authorize?'.$query);
});
#### When Issuing Personal Access Tokens
-If you are issuing personal access tokens using the `User` model's `createToken` method, you may pass the array of desired scopes as the second argument to the method:
+If you are issuing personal access tokens using the `App\Models\User` model's `createToken` method, you may pass the array of desired scopes as the second argument to the method:
$token = $user->createToken('My Token', ['place-orders'])->accessToken;
@@ -993,7 +1040,7 @@ Passport includes two middleware that may be used to verify that an incoming req
#### Check For All Scopes
-The `scopes` middleware may be assigned to a route to verify that the incoming request's access token has *all* of the listed scopes:
+The `scopes` middleware may be assigned to a route to verify that the incoming request's access token has all of the listed scopes:
Route::get('/orders', function () {
// Access token has both "check-status" and "place-orders" scopes...
@@ -1011,7 +1058,7 @@ The `scope` middleware may be assigned to a route to verify that the incoming re
#### Checking Scopes On A Token Instance
-Once an access token authenticated request has entered your application, you may still check if the token has a given scope using the `tokenCan` method on the authenticated `User` instance:
+Once an access token authenticated request has entered your application, you may still check if the token has a given scope using the `tokenCan` method on the authenticated `App\Models\User` instance:
use Illuminate\Http\Request;
@@ -1026,19 +1073,21 @@ Once an access token authenticated request has entered your application, you may
The `scopeIds` method will return an array of all defined IDs / names:
- Laravel\Passport\Passport::scopeIds();
+ use Laravel\Passport\Passport;
+
+ Passport::scopeIds();
The `scopes` method will return an array of all defined scopes as instances of `Laravel\Passport\Scope`:
- Laravel\Passport\Passport::scopes();
+ Passport::scopes();
The `scopesFor` method will return an array of `Laravel\Passport\Scope` instances matching the given IDs / names:
- Laravel\Passport\Passport::scopesFor(['place-orders', 'check-status']);
+ Passport::scopesFor(['place-orders', 'check-status']);
You may determine if a given scope has been defined using the `hasScope` method:
- Laravel\Passport\Passport::hasScope('place-orders');
+ Passport::hasScope('place-orders');
## Consuming Your API With JavaScript
@@ -1054,7 +1103,7 @@ Typically, if you want to consume your API from your JavaScript application, you
> {note} You should ensure that the `CreateFreshApiToken` middleware is the last middleware listed in your middleware stack.
-This Passport middleware will attach a `laravel_token` cookie to your outgoing responses. This cookie contains an encrypted JWT that Passport will use to authenticate API requests from your JavaScript application. The JWT has a lifetime equal to your `session.lifetime` configuration value. Now, you may make requests to your application's API without explicitly passing an access token:
+This middleware will attach a `laravel_token` cookie to your outgoing responses. This cookie contains an encrypted JWT that Passport will use to authenticate API requests from your JavaScript application. The JWT has a lifetime equal to your `session.lifetime` configuration value. Now, since the browser will automatically send the cookie with all subsequent requests, you may make requests to your application's API without explicitly passing an access token:
axios.get('/api/user')
.then(response => {
@@ -1064,7 +1113,7 @@ This Passport middleware will attach a `laravel_token` cookie to your outgoing r
#### Customizing The Cookie Name
-If needed, you can customize the `laravel_token` cookie's name using the `Passport::cookie` method. Typically, this method should be called from the `boot` method of your `AuthServiceProvider`:
+If needed, you can customize the `laravel_token` cookie's name using the `Passport::cookie` method. Typically, this method should be called from the `boot` method of your application's `App\Providers\AuthServiceProvider` class:
/**
* Register any authentication / authorization services.
@@ -1090,7 +1139,7 @@ When using this method of authentication, you will need to ensure a valid CSRF t
## Events
-Passport raises events when issuing access tokens and refresh tokens. You may use these events to prune or revoke other access tokens in your database. You may attach listeners to these events in your application's `EventServiceProvider`:
+Passport raises events when issuing access tokens and refresh tokens. You may use these events to prune or revoke other access tokens in your database. If you would like, you may attach listeners to these events in your application's `App\Providers\EventServiceProvider` class:
/**
* The event listener mappings for the application.
@@ -1115,7 +1164,7 @@ Passport's `actingAs` method may be used to specify the currently authenticated
use App\Models\User;
use Laravel\Passport\Passport;
- public function testServerCreation()
+ public function test_servers_can_be_created()
{
Passport::actingAs(
User::factory()->create(),
@@ -1132,7 +1181,7 @@ Passport's `actingAsClient` method may be used to specify the currently authenti
use Laravel\Passport\Client;
use Laravel\Passport\Passport;
- public function testGetOrders()
+ public function test_orders_can_be_retrieved()
{
Passport::actingAsClient(
Client::factory()->create(),
diff --git a/passwords.md b/passwords.md
index 674335f8d9a..86065586b2b 100644
--- a/passwords.md
+++ b/passwords.md
@@ -11,28 +11,28 @@
## Introduction
-Most web applications provide a way for users to reset their forgotten passwords. Rather than forcing you to re-implement this on each application, Laravel provides convenient methods for sending password reminders and performing password resets.
+Most web applications provide a way for users to reset their forgotten passwords. Rather than forcing you to re-implement this by hand for every application you create, Laravel provides convenient services for sending password reset links and secure resetting passwords.
-> {tip} Want to get started fast? Install [Laravel Jetstream](https://jetstream.laravel.com) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. Jetstream will take care of scaffolding your entire authentication system, including resetting passwords!
+> {tip} Want to get started fast? Install a Laravel [application starter kit](/docs/{{version}}/starter-kits) in a fresh Laravel application. Laravel's starter kits will take care of scaffolding your entire authentication system, including resetting forgotten passwords.
### Model Preparation
-Before using the password reset features of Laravel, your `App\Models\User` model must use the `Illuminate\Notifications\Notifiable` trait. Typically, this trait is automatically included on the default `App\Models\User` model that is included with Laravel.
+Before using the password reset features of Laravel, your application's `App\Models\User` model must use the `Illuminate\Notifications\Notifiable` trait. Typically, this trait is already included on the default `App\Models\User` model that is created with new Laravel applications.
Next, verify that your `App\Models\User` model implements the `Illuminate\Contracts\Auth\CanResetPassword` contract. The `App\Models\User` model included with the framework already implements this interface, and uses the `Illuminate\Auth\Passwords\CanResetPassword` trait to include the methods needed to implement the interface.
### Database Preparation
-A table must be created to store your application's password reset tokens. The migration for this table is included in the default Laravel installation, so you only need to migrate your database to create this table:
+A table must be created to store your application's password reset tokens. The migration for this table is included in the default Laravel application, so you only need to migrate your database to create this table:
php artisan migrate
## Routing
-To properly implement support for allowing users to reset their passwords, we will need to define several routes. First, we will need a pair of routes to handle allowing the user to request a password reset link via their email address. Second, we will need a pair of routes to handle actually resetting the password once the user visits the password reset link that is emailed to them.
+To properly implement support for allowing users to reset their passwords, we will need to define several routes. First, we will need a pair of routes to handle allowing the user to request a password reset link via their email address. Second, we will need a pair of routes to handle actually resetting the password once the user visits the password reset link that is emailed to them and completes the password reset form.
### Requesting The Password Reset Link
@@ -44,14 +44,14 @@ First, we will define the routes that are needed to request password reset links
Route::get('/forgot-password', function () {
return view('auth.forgot-password');
- })->middleware(['guest'])->name('password.request');
+ })->middleware('guest')->name('password.request');
The view that is returned by this route should have a form containing an `email` field, which will allow the user to request a password reset link for a given email address.
#### Handling The Form Submission
-Next, we will define a route will handle the form request from the "forgot password" view. This route will be responsible for validating the email address and sending the password reset request to the corresponding user:
+Next, we will define a route that handles the form submission request from the "forgot password" view. This route will be responsible for validating the email address and sending the password reset request to the corresponding user:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
@@ -66,13 +66,15 @@ Next, we will define a route will handle the form request from the "forgot passw
return $status === Password::RESET_LINK_SENT
? back()->with(['status' => __($status)])
: back()->withErrors(['email' => __($status)]);
- })->middleware(['guest'])->name('password.email');
+ })->middleware('guest')->name('password.email');
Before moving on, let's examine this route in more detail. First, the request's `email` attribute is validated. Next, we will use Laravel's built-in "password broker" (via the `Password` facade) to send a password reset link to the user. The password broker will take care of retrieving the user by the given field (in this case, the email address) and sending the user a password reset link via Laravel's built-in [notification system](/docs/{{version}}/notifications).
The `sendResetLink` method returns a "status" slug. This status may be translated using Laravel's [localization](/docs/{{version}}/localization) helpers in order to display a user-friendly message to the user regarding the status of their request. The translation of the password reset status is determined by your application's `resources/lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file.
-> {tip} When manually implementing password resets, you are required to define the contents of the views and routes yourself. If you would like scaffolding that includes all necessary authentication and verification logic, check out [Laravel Jetstream](https://jetstream.laravel.com).
+You may wondering how Laravel knows how to retrieve the user record from your application's database when calling the `Password` facade's `sendResetLink` method. The Laravel password broker utilizes your authentication system's "user providers" to retrieve database records. The user provider used by the password broker is configured within the `passwords` configuration array of your `config/auth.php` configuration file. To learn more about writing custom user providers, consult the [authentication documentation](/docs/{{version}}/authentication#adding-custom-user-providers)
+
+> {tip} When manually implementing password resets, you are required to define the contents of the views and routes yourself. If you would like scaffolding that includes all necessary authentication and verification logic, check out the [Laravel application starter kits](/docs/{{version}}/starter-kits).
### Resetting The Password
@@ -84,9 +86,9 @@ Next, we will define the routes necessary to actually reset the password once th
Route::get('/reset-password/{token}', function ($token) {
return view('auth.reset-password', ['token' => $token]);
- })->middleware(['guest'])->name('password.reset');
+ })->middleware('guest')->name('password.reset');
-The view that is returned by this route should have a form containing an `email` field, a `password` field, a `password_confirmation` field, and a hidden `token` field, which should contain the value of the secret token received by our route.
+The view that is returned by this route should display a form containing an `email` field, a `password` field, a `password_confirmation` field, and a hidden `token` field, which should contain the value of the secret `$token` received by our route.
#### Handling The Form Submission
@@ -121,49 +123,57 @@ Of course, we need to define a route to actually handle the password reset form
return $status == Password::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status))
- : back()->withErrors(['email' => __($status)]);
- })->middleware(['guest'])->name('password.update');
+ : back()->withErrors(['email' => [__($status)]]);
+ })->middleware('guest')->name('password.update');
Before moving on, let's examine this route in more detail. First, the request's `token`, `email`, and `password` attributes are validated. Next, we will use Laravel's built-in "password broker" (via the `Password` facade) to validate the password reset request credentials.
-If the token, email address, and password given to the password broker are valid, the Closure passed to the `reset` method will be invoked. Within this Closure, which receives the user instance and the plain-text password, we may update the user's password in the database.
+If the token, email address, and password given to the password broker are valid, the closure passed to the `reset` method will be invoked. Within this closure, which receives the user instance and the plain-text password provided to the password reset form, we may update the user's password in the database.
The `reset` method returns a "status" slug. This status may be translated using Laravel's [localization](/docs/{{version}}/localization) helpers in order to display a user-friendly message to the user regarding the status of their request. The translation of the password reset status is determined by your application's `resources/lang/{lang}/passwords.php` language file. An entry for each possible value of the status slug is located within the `passwords` language file.
+Before moving on, you may wondering how Laravel knows how to retrieve the user record from your application's database when calling the `Password` facade's `reset` method. The Laravel password broker utilizes your authentication system's "user providers" to retrieve database records. The user provider used by the password broker is configured within the `passwords` configuration array of your `config/auth.php` configuration file. To learn more about writing custom user providers, consult the [authentication documentation](/docs/{{version}}/authentication#adding-custom-user-providers)
+
## Customization
#### Reset Link Customization
-You may customize the password reset link URL using the `createUrlUsing` method provided by the `ResetPassword` notification class. This method accepts a Closure which receives the user instance that is receiving the notification as well as the password reset link token. Typically, you should call this method from a service provider's `boot` method:
+You may customize the password reset link URL using the `createUrlUsing` method provided by the `ResetPassword` notification class. This method accepts a closure which receives the user instance that is receiving the notification as well as the password reset link token. Typically, you should call this method from your `App\Providers\AuthServiceProvider` service provider's `boot` method:
use Illuminate\Auth\Notifications\ResetPassword;
/**
- * Bootstrap any application services.
+ * Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
- ResetPassword::createUrlUsing(function ($notifiable, string $token) {
- return 'https://example.com/auth/reset-password?token='.$token;
+ $this->registerPolicies();
+
+ ResetPassword::createUrlUsing(function ($user, string $token) {
+ return 'https://example.com/reset-password?token='.$token;
});
}
#### Reset Email Customization
-You may easily modify the notification class used to send the password reset link to the user. To get started, override the `sendPasswordResetNotification` method on your `User` model. Within this method, you may send the notification using any notification class you choose. The password reset `$token` is the first argument received by the method:
+You may easily modify the notification class used to send the password reset link to the user. To get started, override the `sendPasswordResetNotification` method on your `App\Models\User` model. Within this method, you may send the notification using any [notification class](/docs/{{version}}/notifications) of your own creation. The password reset `$token` is the first argument received by the method. You may use this `$token` to build the password reset URL of your choice and send your notification to the user:
+
+ use App\Notifications\ResetPasswordNotification;
/**
- * Send the password reset notification.
+ * Send a password reset notification to the user.
*
* @param string $token
* @return void
*/
public function sendPasswordResetNotification($token)
{
- $this->notify(new ResetPasswordNotification($token));
+ $url = 'https://example.com/reset-password?token='.$token;
+
+ $this->notify(new ResetPasswordNotification($url));
}
diff --git a/providers.md b/providers.md
index 32468c79b92..43d61f38ca5 100644
--- a/providers.md
+++ b/providers.md
@@ -14,10 +14,12 @@ Service providers are the central place of all Laravel application bootstrapping
But, what do we mean by "bootstrapped"? In general, we mean **registering** things, including registering service container bindings, event listeners, middleware, and even routes. Service providers are the central place to configure your application.
-If you open the `config/app.php` file included with Laravel, you will see a `providers` array. These are all of the service provider classes that will be loaded for your application. Note that many of these are "deferred" providers, meaning they will not be loaded on every request, but only when the services they provide are actually needed.
+If you open the `config/app.php` file included with Laravel, you will see a `providers` array. These are all of the service provider classes that will be loaded for your application. By default, a set of Laravel core service providers are listed in this array. These providers bootstrap the core Laravel components, such as the mailer, queue, cache, and others. Many of these providers are "deferred" providers, meaning they will not be loaded on every request, but only when the services they provide are actually needed.
In this overview you will learn how to write your own service providers and register them with your Laravel application.
+> {tip} If you would like to learn more about how Laravel handles requests and works internally, check out our documentation on the Laravel [request lifecycle](/docs/{{version}}/lifecycle).
+
## Writing Service Providers
@@ -38,8 +40,8 @@ Let's take a look at a basic service provider. Within any of your service provid
namespace App\Providers;
+ use App\Services\Riak\Connection;
use Illuminate\Support\ServiceProvider;
- use Riak\Connection;
class RiakServiceProvider extends ServiceProvider
{
@@ -56,7 +58,7 @@ Let's take a look at a basic service provider. Within any of your service provid
}
}
-This service provider only defines a `register` method, and uses that method to define an implementation of `Riak\Connection` in the service container. If you don't understand how the service container works, check out [its documentation](/docs/{{version}}/container).
+This service provider only defines a `register` method, and uses that method to define an implementation of `App\Services\Riak\Connection` in the service container. If you you're not yet familiar with Laravel's service container, check out [its documentation](/docs/{{version}}/container).
#### The `bindings` And `singletons` Properties
@@ -105,6 +107,7 @@ So, what if we need to register a [view composer](/docs/{{version}}/views#view-c
namespace App\Providers;
+ use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
@@ -116,7 +119,7 @@ So, what if we need to register a [view composer](/docs/{{version}}/views#view-c
*/
public function boot()
{
- view()->composer('view', function () {
+ View::composer('view', function () {
//
});
}
@@ -129,9 +132,15 @@ You may type-hint dependencies for your service provider's `boot` method. The [s
use Illuminate\Contracts\Routing\ResponseFactory;
+ /**
+ * Bootstrap any application services.
+ *
+ * @param \Illuminate\Contracts\Routing\ResponseFactory
+ * @return void
+ */
public function boot(ResponseFactory $response)
{
- $response->macro('caps', function ($value) {
+ $response->macro('serialized', function ($value) {
//
});
}
@@ -162,9 +171,9 @@ To defer the loading of a provider, implement the `\Illuminate\Contracts\Support
namespace App\Providers;
+ use App\Services\Riak\Connection;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
- use Riak\Connection;
class RiakServiceProvider extends ServiceProvider implements DeferrableProvider
{
diff --git a/queries.md b/queries.md
index 7a19458bb12..626dcf91744 100644
--- a/queries.md
+++ b/queries.md
@@ -1,44 +1,52 @@
# Database: Query Builder
- [Introduction](#introduction)
-- [Retrieving Results](#retrieving-results)
+- [Running Database Queries](#running-database-queries)
- [Chunking Results](#chunking-results)
- [Aggregates](#aggregates)
-- [Selects](#selects)
+- [Select Statements](#select-statements)
- [Raw Expressions](#raw-expressions)
- [Joins](#joins)
- [Unions](#unions)
-- [Where Clauses](#where-clauses)
- - [Parameter Grouping](#parameter-grouping)
+- [Basic Where Clauses](#basic-where-clauses)
+ - [Where Clauses](#where-clauses)
+ - [Or Where Clauses](#or-where-clauses)
+ - [JSON Where Clauses](#json-where-clauses)
+ - [Additional Where Clauses](#additional-where-clauses)
+ - [Logical Grouping](#logical-grouping)
+- [Advanced Where Clauses](#advanced-where-clauses)
- [Where Exists Clauses](#where-exists-clauses)
- [Subquery Where Clauses](#subquery-where-clauses)
- - [JSON Where Clauses](#json-where-clauses)
- [Ordering, Grouping, Limit & Offset](#ordering-grouping-limit-and-offset)
+ - [Ordering](#ordering)
+ - [Grouping](#grouping)
+ - [Limit & Offset](#limit-and-offset)
- [Conditional Clauses](#conditional-clauses)
-- [Inserts](#inserts)
-- [Updates](#updates)
+- [Insert Statements](#insert-statements)
+ - [Upserts](#upserts)
+- [Update Statements](#update-statements)
- [Updating JSON Columns](#updating-json-columns)
- [Increment & Decrement](#increment-and-decrement)
-- [Deletes](#deletes)
+- [Delete Statements](#delete-statements)
- [Pessimistic Locking](#pessimistic-locking)
- [Debugging](#debugging)
## Introduction
-Laravel's database query builder provides a convenient, fluent interface to creating and running database queries. It can be used to perform most database operations in your application and works on all supported database systems.
+Laravel's database query builder provides a convenient, fluent interface to creating and running database queries. It can be used to perform most database operations in your application and works perfectly with all of Laravel's supported database systems.
-The Laravel query builder uses PDO parameter binding to protect your application against SQL injection attacks. There is no need to clean strings being passed as bindings.
+The Laravel query builder uses PDO parameter binding to protect your application against SQL injection attacks. There is no need to clean or sanitize strings passed to the query builder as query bindings.
-> {note} PDO does not support binding column names. Therefore, you should never allow user input to dictate the column names referenced by your queries, including "order by" columns, etc. If you must allow the user to select certain columns to query against, always validate the column names against a white-list of allowed columns.
+> {note} PDO does not support binding column names. Therefore, you should never allow user input to dictate the column names referenced by your queries, including "order by" columns.
-
-## Retrieving Results
+
+## Running Database Queries
#### Retrieving All Rows From A Table
-You may use the `table` method on the `DB` facade to begin a query. The `table` method returns a fluent query builder instance for the given table, allowing you to chain more constraints onto the query and then finally get the results using the `get` method:
+You may use the `table` method provided by the `DB` facade to begin a query. The `table` method returns a fluent query builder instance for the given table, allowing you to chain more constraints onto the query and then finally retrieve the results of the query using the `get` method:
get();
foreach ($users as $user) {
echo $user->name;
}
+> {tip} Laravel collections provide a variety of extremely powerful methods for mapping and reducing data. For more information on Laravel colletions, check out the [collection documentation](/docs/{{version}}/collections).
+
#### Retrieving A Single Row / Column From A Table
-If you just need to retrieve a single row from the database table, you may use the `first` method. This method will return a single `stdClass` object:
+If you just need to retrieve a single row from a database table, you may use the `DB` facade's `first` method. This method will return a single `stdClass` object:
$user = DB::table('users')->where('name', 'John')->first();
- echo $user->name;
+ return $user->email;
-If you don't even need an entire row, you may extract a single value from a record using the `value` method. This method will return the value of the column directly:
+If you don't need an entire row, you may extract a single value from a record using the `value` method. This method will return the value of the column directly:
$email = DB::table('users')->where('name', 'John')->value('email');
@@ -88,26 +102,30 @@ To retrieve a single row by its `id` column value, use the `find` method:
#### Retrieving A List Of Column Values
-If you would like to retrieve a Collection containing the values of a single column, you may use the `pluck` method. In this example, we'll retrieve a Collection of role titles:
+If you would like to retrieve an `Illuminate\Support\Collection` instance containing the values of a single column, you may use the `pluck` method. In this example, we'll retrieve a collection of role titles:
+
+ use Illuminate\Support\Facades\DB;
- $titles = DB::table('roles')->pluck('title');
+ $titles = DB::table('users')->pluck('title');
foreach ($titles as $title) {
echo $title;
}
- You may also specify a custom key column for the returned Collection:
+ You may specify the column that the resulting collection should use as its keys by providing a second argument to the `pluck` method:
- $roles = DB::table('roles')->pluck('title', 'name');
+ $titles = DB::table('users')->pluck('title', 'name');
- foreach ($roles as $name => $title) {
+ foreach ($titles as $name => $title) {
echo $title;
}
### Chunking Results
-If you need to work with thousands of database records, consider using the `chunk` method. This method retrieves a small chunk of the results at a time and feeds each chunk into a `Closure` for processing. This method is very useful for writing [Artisan commands](/docs/{{version}}/artisan) that process thousands of records. For example, let's work with the entire `users` table in chunks of 100 records at a time:
+If you need to work with thousands of database records, consider using the `chunk` method provided by the `DB` facade. This method retrieves a small chunk of results at a time and feeds each chunk into a closure for processing. For example, let's retrieve the entire `users` table in chunks of 100 records at a time:
+
+ use Illuminate\Support\Facades\DB;
DB::table('users')->orderBy('id')->chunk(100, function ($users) {
foreach ($users as $user) {
@@ -115,7 +133,7 @@ If you need to work with thousands of database records, consider using the `chun
}
});
-You may stop further chunks from being processed by returning `false` from the `Closure`:
+You may stop further chunks from being processed by returning `false` from the closure:
DB::table('users')->orderBy('id')->chunk(100, function ($users) {
// Process the records...
@@ -123,7 +141,7 @@ You may stop further chunks from being processed by returning `false` from the `
return false;
});
-If you are updating database records while chunking results, your chunk results could change in unexpected ways. So, when updating records while chunking, it is always best to use the `chunkById` method instead. This method will automatically paginate the results based on the record's primary key:
+If you are updating database records while chunking results, your chunk results could change in unexpected ways. If you plan to update the retrieved records while chunking, it is always best to use the `chunkById` method instead. This method will automatically paginate the results based on the record's primary key:
DB::table('users')->where('active', false)
->chunkById(100, function ($users) {
@@ -139,13 +157,15 @@ If you are updating database records while chunking results, your chunk results
### Aggregates
-The query builder also provides a variety of aggregate methods such as `count`, `max`, `min`, `avg`, and `sum`. You may call any of these methods after constructing your query:
+The query builder also provides a variety of methods for retrieving aggregate values like `count`, `max`, `min`, `avg`, and `sum`. You may call any of these methods after constructing your query:
+
+ use Illuminate\Support\Facades\DB;
$users = DB::table('users')->count();
$price = DB::table('orders')->max('price');
-You may combine these methods with other clauses:
+Of course, you may combine these methods with other clauses to fine-tune how your aggregate value is calculated:
$price = DB::table('orders')
->where('finalized', 1)
@@ -156,19 +176,27 @@ You may combine these methods with other clauses:
Instead of using the `count` method to determine if any records exist that match your query's constraints, you may use the `exists` and `doesntExist` methods:
- return DB::table('orders')->where('finalized', 1)->exists();
+ if (DB::table('orders')->where('finalized', 1)->exists()) {
+ // ...
+ }
- return DB::table('orders')->where('finalized', 1)->doesntExist();
+ if (DB::table('orders')->where('finalized', 1)->doesntExist()) {
+ // ...
+ }
-
-## Selects
+
+## Select Statements
#### Specifying A Select Clause
-You may not always want to select all columns from a database table. Using the `select` method, you can specify a custom `select` clause for the query:
+You may not always want to select all columns from a database table. Using the `select` method, you can specify a custom "select" clause for the query:
- $users = DB::table('users')->select('name', 'email as user_email')->get();
+ use Illuminate\Support\Facades\DB;
+
+ $users = DB::table('users')
+ ->select('name', 'email as user_email')
+ ->get();
The `distinct` method allows you to force the query to return distinct results:
@@ -183,20 +211,20 @@ If you already have a query builder instance and you wish to add a column to its
## Raw Expressions
-Sometimes you may need to use a raw expression in a query. To create a raw expression, you may use the `DB::raw` method:
+Sometimes you may need to insert an arbitrary string into a query. To create a raw string expression, you may use the `raw` method provided by the `DB` facade:
$users = DB::table('users')
- ->select(DB::raw('count(*) as user_count, status'))
- ->where('status', '<>', 1)
- ->groupBy('status')
- ->get();
+ ->select(DB::raw('count(*) as user_count, status'))
+ ->where('status', '<>', 1)
+ ->groupBy('status')
+ ->get();
-> {note} Raw statements will be injected into the query as strings, so you should be extremely careful to not create SQL injection vulnerabilities.
+> {note} Raw statements will be injected into the query as strings, so you should be extremely careful to avoid creating SQL injection vulnerabilities.
### Raw Methods
-Instead of using `DB::raw`, you may also use the following methods to insert a raw expression into various parts of your query.
+Instead of using the `DB::raw` method, you may also use the following methods to insert a raw expression into various parts of your query. **Remember, Laravel can not guarantee that any query using raw expressions is protected against SQL injection vulnerabilities.**
#### `selectRaw`
@@ -210,7 +238,7 @@ The `selectRaw` method can be used in place of `addSelect(DB::raw(...))`. This m
#### `whereRaw / orWhereRaw`
-The `whereRaw` and `orWhereRaw` methods can be used to inject a raw `where` clause into your query. These methods accept an optional array of bindings as their second argument:
+The `whereRaw` and `orWhereRaw` methods can be used to inject a raw "where" clause into your query. These methods accept an optional array of bindings as their second argument:
$orders = DB::table('orders')
->whereRaw('price > IF(state = "TX", ?, 100)', [200])
@@ -219,7 +247,7 @@ The `whereRaw` and `orWhereRaw` methods can be used to inject a raw `where` clau
#### `havingRaw / orHavingRaw`
-The `havingRaw` and `orHavingRaw` methods may be used to set a raw string as the value of the `having` clause. These methods accept an optional array of bindings as their second argument:
+The `havingRaw` and `orHavingRaw` methods may be used to provide a raw string as the value of the "having" clause. These methods accept an optional array of bindings as their second argument:
$orders = DB::table('orders')
->select('department', DB::raw('SUM(price) as total_sales'))
@@ -230,7 +258,7 @@ The `havingRaw` and `orHavingRaw` methods may be used to set a raw string as the
#### `orderByRaw`
-The `orderByRaw` method may be used to set a raw string as the value of the `order by` clause:
+The `orderByRaw` method may be used to provide a raw string as the value of the "order by" clause:
$orders = DB::table('orders')
->orderByRaw('updated_at - created_at DESC')
@@ -239,7 +267,7 @@ The `orderByRaw` method may be used to set a raw string as the value of the `ord
### `groupByRaw`
-The `groupByRaw` method may be used to set a raw string as the value of the `group by` clause:
+The `groupByRaw` method may be used to provide a raw string as the value of the `group by` clause:
$orders = DB::table('orders')
->select('city', 'state')
@@ -252,7 +280,9 @@ The `groupByRaw` method may be used to set a raw string as the value of the `gro
#### Inner Join Clause
-The query builder may also be used to write join statements. To perform a basic "inner join", you may use the `join` method on a query builder instance. The first argument passed to the `join` method is the name of the table you need to join to, while the remaining arguments specify the column constraints for the join. You can even join to multiple tables in a single query:
+The query builder may also be used to add join clauses to your queries. To perform a basic "inner join", you may use the `join` method on a query builder instance. The first argument passed to the `join` method is the name of the table you need to join to, while the remaining arguments specify the column constraints for the join. You may even join to multiple tables in a single query:
+
+ use Illuminate\Support\Facades\DB;
$users = DB::table('users')
->join('contacts', 'users.id', '=', 'contacts.user_id')
@@ -276,7 +306,7 @@ If you would like to perform a "left join" or "right join" instead of an "inner
#### Cross Join Clause
-To perform a "cross join" use the `crossJoin` method with the name of the table you wish to cross join to. Cross joins generate a cartesian product between the first table and the joined table:
+You may use the `crossJoin` method to perform a "cross join". Cross joins generate a cartesian product between the first table and the joined table:
$sizes = DB::table('sizes')
->crossJoin('colors')
@@ -285,7 +315,7 @@ To perform a "cross join" use the `crossJoin` method with the name of the table
#### Advanced Join Clauses
-You may also specify more advanced join clauses. To get started, pass a `Closure` as the second argument into the `join` method. The `Closure` will receive a `JoinClause` object which allows you to specify constraints on the `join` clause:
+You may also specify more advanced join clauses. To get started, pass a closure as the second argument to the `join` method. The closure will receive a `Illuminate\Database\Query\JoinClause` instance which allows you to specify constraints on the "join" clause:
DB::table('users')
->join('contacts', function ($join) {
@@ -293,7 +323,7 @@ You may also specify more advanced join clauses. To get started, pass a `Closure
})
->get();
-If you would like to use a "where" style clause on your joins, you may use the `where` and `orWhere` methods on a join. Instead of comparing two columns, these methods will compare the column against a value:
+If you would like to use a "where" clause on your joins, you may use the `where` and `orWhere` methods provided by the `JoinClause` instance. Instead of comparing two columns, these methods will compare the column against a value:
DB::table('users')
->join('contacts', function ($join) {
@@ -305,7 +335,7 @@ If you would like to use a "where" style clause on your joins, you may use the `
#### Subquery Joins
-You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a query to a subquery. Each of these methods receive three arguments: the subquery, its table alias, and a Closure that defines the related columns:
+You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a query to a subquery. Each of these methods receive three arguments: the subquery, its table alias, and a closure that defines the related columns. In this example, we will retrieve a collection of users where each user record also contains the `created_at` timestamp of the user's most recently published blog post:
$latestPosts = DB::table('posts')
->select('user_id', DB::raw('MAX(created_at) as last_post_created_at'))
@@ -320,7 +350,9 @@ You may use the `joinSub`, `leftJoinSub`, and `rightJoinSub` methods to join a q
## Unions
-The query builder also provides a quick way to "union" two or more queries together. For example, you may create an initial query and use the `union` method to union it with more queries:
+The query builder also provides a convenient method to "union" two or more queries together. For example, you may create an initial query and use the `union` method to union it with more queries:
+
+ use Illuminate\Support\Facades\DB;
$first = DB::table('users')
->whereNull('first_name');
@@ -330,25 +362,28 @@ The query builder also provides a quick way to "union" two or more queries toget
->union($first)
->get();
-> {tip} The `unionAll` method is also available and has the same method signature as `union`.
+In addition to the `union` method, the query builder provides a `unionAll` method. Queries that are combined using the `unionAll` method will not have their duplicate results removed. The `unionAll` method has the same method signature as the `union` method.
-
-## Where Clauses
+
+## Basic Where Clauses
-
-#### Simple Where Clauses
+
+### Where Clauses
-You may use the `where` method on a query builder instance to add `where` clauses to the query. The most basic call to `where` requires three arguments. The first argument is the name of the column. The second argument is an operator, which can be any of the database's supported operators. Finally, the third argument is the value to evaluate against the column.
+You may use the query builder's `where` method to add "where" clauses to the query. The most basic call to the `where` method requires three arguments. The first argument is the name of the column. The second argument is an operator, which can be any of the database's supported operators. The third argument is the value to compare against the column's value.
-For example, here is a query that verifies the value of the "votes" column is equal to 100:
+For example, the following query retrieves users where the value of the `votes` column is equal to `100` and the value of the `age` column is greater than `35`:
- $users = DB::table('users')->where('votes', '=', 100)->get();
+ $users = DB::table('users')
+ ->where('votes', '=', 100)
+ ->where('age', '>', 35)
+ ->get();
-For convenience, if you want to verify that a column is equal to a given value, you may pass the value directly as the second argument to the `where` method:
+For convenience, if you want to verify that a column is `=` to a given value, you may pass the value as the second argument to the `where` method. Laravel will assume you would like to use the `=` operator:
$users = DB::table('users')->where('votes', 100)->get();
-You may use a variety of other operators when writing a `where` clause:
+As previously mentioned, you may use any operator that is supported by your database system:
$users = DB::table('users')
->where('votes', '>=', 100)
@@ -362,24 +397,24 @@ You may use a variety of other operators when writing a `where` clause:
->where('name', 'like', 'T%')
->get();
-You may also pass an array of conditions to the `where` function:
+You may also pass an array of conditions to the `where` function. Each element of the array should be an array containing the three arguments typically passed to the `where` method:
$users = DB::table('users')->where([
['status', '=', '1'],
['subscribed', '<>', '1'],
])->get();
-
-#### Or Statements
+
+### Or Where Clauses
-You may chain where constraints together as well as add `or` clauses to the query. The `orWhere` method accepts the same arguments as the `where` method:
+When chaining together calls to the query builder's `where` method, the "where" clauses will be joined together using the `and` operator. However, you may use the `orWhere` method to join a clause to the query using the `or` operator. The `orWhere` method accepts the same arguments as the `where` method:
$users = DB::table('users')
->where('votes', '>', 100)
->orWhere('name', 'John')
->get();
-If you need to group an "or" condition within parentheses, you may pass a Closure as the first argument to the `orWhere` method:
+If you need to group an "or" condition within parentheses, you may pass a closure as the first argument to the `orWhere` method:
$users = DB::table('users')
->where('votes', '>', 100)
@@ -389,10 +424,47 @@ If you need to group an "or" condition within parentheses, you may pass a Closur
})
->get();
- // SQL: select * from users where votes > 100 or (name = 'Abigail' and votes > 50)
+The example above will produce the following SQL:
+
+```sql
+select * from users where votes > 100 or (name = 'Abigail' and votes > 50)
+```
+
+> {note} You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied.
+
+
+### JSON Where Clauses
+
+Laravel also supports querying JSON column types on databases that provide support for JSON column types. Currently, this includes MySQL 5.7+, PostgreSQL, SQL Server 2016, and SQLite 3.9.0 (with the [JSON1 extension](https://www.sqlite.org/json1.html)). To query a JSON column, use the `->` operator:
+
+ $users = DB::table('users')
+ ->where('preferences->dining->meal', 'salad')
+ ->get();
+
+You may use `whereJsonContains` to query JSON arrays. This feature is not supported by the SQLite database:
+
+ $users = DB::table('users')
+ ->whereJsonContains('options->languages', 'en')
+ ->get();
+
+If your application uses the MySQL or PostgreSQL databases, you may pass an array of values to the `whereJsonContains` method:
+
+ $users = DB::table('users')
+ ->whereJsonContains('options->languages', ['en', 'de'])
+ ->get();
+
+You may use `whereJsonLength` method to query JSON arrays by their length:
+
+ $users = DB::table('users')
+ ->whereJsonLength('options->languages', 0)
+ ->get();
+
+ $users = DB::table('users')
+ ->whereJsonLength('options->languages', '>', 1)
+ ->get();
-#### Additional Where Clauses
+### Additional Where Clauses
**whereBetween / orWhereBetween**
@@ -418,27 +490,27 @@ The `whereIn` method verifies that a given column's value is contained within th
->whereIn('id', [1, 2, 3])
->get();
-The `whereNotIn` method verifies that the given column's value is **not** contained in the given array:
+The `whereNotIn` method verifies that the given column's value is not contained in the given array:
$users = DB::table('users')
->whereNotIn('id', [1, 2, 3])
->get();
-> {note} If you are adding a huge array of integer bindings to your query, the `whereIntegerInRaw` or `whereIntegerNotInRaw` methods may be used to greatly reduce your memory usage.
+> {note} If you are adding a large array of integer bindings to your query, the `whereIntegerInRaw` or `whereIntegerNotInRaw` methods may be used to greatly reduce your memory usage.
**whereNull / whereNotNull / orWhereNull / orWhereNotNull**
The `whereNull` method verifies that the value of the given column is `NULL`:
$users = DB::table('users')
- ->whereNull('updated_at')
- ->get();
+ ->whereNull('updated_at')
+ ->get();
The `whereNotNull` method verifies that the column's value is not `NULL`:
$users = DB::table('users')
- ->whereNotNull('updated_at')
- ->get();
+ ->whereNotNull('updated_at')
+ ->get();
**whereDate / whereMonth / whereDay / whereYear / whereTime**
@@ -448,13 +520,13 @@ The `whereDate` method may be used to compare a column's value against a date:
->whereDate('created_at', '2016-12-31')
->get();
-The `whereMonth` method may be used to compare a column's value against a specific month of a year:
+The `whereMonth` method may be used to compare a column's value against a specific month:
$users = DB::table('users')
->whereMonth('created_at', '12')
->get();
-The `whereDay` method may be used to compare a column's value against a specific day of a month:
+The `whereDay` method may be used to compare a column's value against a specific day of the month:
$users = DB::table('users')
->whereDay('created_at', '31')
@@ -480,13 +552,13 @@ The `whereColumn` method may be used to verify that two columns are equal:
->whereColumn('first_name', 'last_name')
->get();
-You may also pass a comparison operator to the method:
+You may also pass a comparison operator to the `whereColumn` method:
$users = DB::table('users')
->whereColumn('updated_at', '>', 'created_at')
->get();
-The `whereColumn` method can also be passed an array of multiple conditions. These conditions will be joined using the `and` operator:
+You may also pass an array of column comparisons to the `whereColumn` method. These conditions will be joined using the `and` operator:
$users = DB::table('users')
->whereColumn([
@@ -494,10 +566,10 @@ The `whereColumn` method can also be passed an array of multiple conditions. The
['updated_at', '>', 'created_at'],
])->get();
-
-### Parameter Grouping
+
+### Logical Grouping
-Sometimes you may need to create more advanced where clauses such as "where exists" clauses or nested parameter groupings. The Laravel query builder can handle these as well. To get started, let's look at an example of grouping constraints within parenthesis:
+Sometimes you may need to group several "where" clauses within parentheses in order to achieve your query's desired logical grouping. In fact, you should generally always group calls to the `orWhere` method in parentheses in order to avoid unexpected query behavior. To accomplish this, you may pass a closure to the `where` method:
$users = DB::table('users')
->where('name', '=', 'John')
@@ -507,95 +579,72 @@ Sometimes you may need to create more advanced where clauses such as "where exis
})
->get();
-As you can see, passing a `Closure` into the `where` method instructs the query builder to begin a constraint group. The `Closure` will receive a query builder instance which you can use to set the constraints that should be contained within the parenthesis group. The example above will produce the following SQL:
+As you can see, passing a closure into the `where` method instructs the query builder to begin a constraint group. The closure will receive a query builder instance which you can use to set the constraints that should be contained within the parenthesis group. The example above will produce the following SQL:
+
+```sql
+select * from users where name = 'John' and (votes > 100 or title = 'Admin')
+```
- select * from users where name = 'John' and (votes > 100 or title = 'Admin')
+> {note} You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied.
-> {tip} You should always group `orWhere` calls in order to avoid unexpected behavior when global scopes are applied.
+
+### Advanced Where Clauses
### Where Exists Clauses
-The `whereExists` method allows you to write `where exists` SQL clauses. The `whereExists` method accepts a `Closure` argument, which will receive a query builder instance allowing you to define the query that should be placed inside of the "exists" clause:
+The `whereExists` method allows you to write "where exists" SQL clauses. The `whereExists` method accepts a closure which will receive a query builder instance, allowing you to define the query that should be placed inside of the "exists" clause:
$users = DB::table('users')
->whereExists(function ($query) {
$query->select(DB::raw(1))
->from('orders')
- ->whereRaw('orders.user_id = users.id');
+ ->whereColumn('orders.user_id', 'users.id');
})
->get();
The query above will produce the following SQL:
- select * from users
- where exists (
- select 1 from orders where orders.user_id = users.id
- )
+```sql
+select * from users
+where exists (
+ select 1
+ from orders
+ where orders.user_id = users.id
+)
+```
### Subquery Where Clauses
-Sometimes you may need to construct a where clause that compares the results of a subquery to a given value. You may accomplish this by passing a Closure and a value to the `where` method. For example, the following query will retrieve all users who have a recent "membership" of a given type;
+Sometimes you may need to construct a "where" clause that compares the results of a subquery to a given value. You may accomplish this by passing a closure and a value to the `where` method. For example, the following query will retrieve all users who have a recent "membership" of a given type;
use App\Models\User;
$users = User::where(function ($query) {
$query->select('type')
->from('membership')
- ->whereColumn('user_id', 'users.id')
- ->orderByDesc('start_date')
+ ->whereColumn('membership.user_id', 'users.id')
+ ->orderByDesc('membership.start_date')
->limit(1);
}, 'Pro')->get();
-
-### JSON Where Clauses
-
-Laravel also supports querying JSON column types on databases that provide support for JSON column types. Currently, this includes MySQL 5.7, PostgreSQL, SQL Server 2016, and SQLite 3.9.0 (with the [JSON1 extension](https://www.sqlite.org/json1.html)). To query a JSON column, use the `->` operator:
-
- $users = DB::table('users')
- ->where('options->language', 'en')
- ->get();
-
- $users = DB::table('users')
- ->where('preferences->dining->meal', 'salad')
- ->get();
-
-You may use `whereJsonContains` to query JSON arrays (not supported on SQLite):
-
- $users = DB::table('users')
- ->whereJsonContains('options->languages', 'en')
- ->get();
-
-MySQL and PostgreSQL support `whereJsonContains` with multiple values:
-
- $users = DB::table('users')
- ->whereJsonContains('options->languages', ['en', 'de'])
- ->get();
-
-You may use `whereJsonLength` to query JSON arrays by their length:
-
- $users = DB::table('users')
- ->whereJsonLength('options->languages', 0)
- ->get();
-
- $users = DB::table('users')
- ->whereJsonLength('options->languages', '>', 1)
- ->get();
-
## Ordering, Grouping, Limit & Offset
+
+### Ordering
+
-#### orderBy
+#### The `orderBy` Method
-The `orderBy` method allows you to sort the result of the query by a given column. The first argument to the `orderBy` method should be the column you wish to sort by, while the second argument controls the direction of the sort and may be either `asc` or `desc`:
+The `orderBy` method allows you to sort the results of the query by a given column. The first argument accepted by the `orderBy` method should be the column you wish to sort by, while the second argument determines the direction of the sort and may be either `asc` or `desc`:
$users = DB::table('users')
->orderBy('name', 'desc')
->get();
-If you need to sort by multiple columns, you may invoke `orderBy` as many times as needed:
+To sort by multiple columns, you may simply invoke `orderBy` as many times as necessary:
$users = DB::table('users')
->orderBy('name', 'desc')
@@ -603,16 +652,16 @@ If you need to sort by multiple columns, you may invoke `orderBy` as many times
->get();
-#### latest / oldest
+#### The `latest` & `oldest` Methods
-The `latest` and `oldest` methods allow you to easily order results by date. By default, result will be ordered by the `created_at` column. Or, you may pass the column name that you wish to sort by:
+The `latest` and `oldest` methods allow you to easily order results by date. By default, result will be ordered by the table's `created_at` column. Or, you may pass the column name that you wish to sort by:
$user = DB::table('users')
->latest()
->first();
-
-#### inRandomOrder
+
+#### Random Ordering
The `inRandomOrder` method may be used to sort the query results randomly. For example, you may use this method to fetch a random user:
@@ -620,25 +669,28 @@ The `inRandomOrder` method may be used to sort the query results randomly. For e
->inRandomOrder()
->first();
-
-#### reorder
+
+#### Removing Existing Orderings
-The `reorder` method allows you to remove all the existing orders and optionally apply a new order. For example, you can remove all the existing orders:
+The `reorder` method removes all of the "order by" clauses that have previously been applied to the query:
$query = DB::table('users')->orderBy('name');
$unorderedUsers = $query->reorder()->get();
-To remove all existing orders and apply a new order, provide the column and direction as arguments to the method:
+You may pass a column and direction when calling the `reorder` method in order to remove all existing "order by "clauses" and apply an entirely new order to the query:
$query = DB::table('users')->orderBy('name');
$usersOrderedByEmail = $query->reorder('email', 'desc')->get();
+
+### Grouping
+
-#### groupBy / having
+#### The `groupBy` & `having` Methods
-The `groupBy` and `having` methods may be used to group the query results. The `having` method's signature is similar to that of the `where` method:
+As you might expect, the `groupBy` and `having` methods may be used to group the query results. The `having` method's signature is similar to that of the `where` method:
$users = DB::table('users')
->groupBy('account_id')
@@ -652,16 +704,19 @@ You may pass multiple arguments to the `groupBy` method to group by multiple col
->having('account_id', '>', 100)
->get();
-For more advanced `having` statements, see the [`havingRaw`](#raw-methods) method.
+To build more advanced `having` statements, see the [`havingRaw`](#raw-methods) method.
+
+
+### Limit & Offset
-#### skip / take
+#### The `skip` & `take` Methods
-To limit the number of results returned from the query, or to skip a given number of results in the query, you may use the `skip` and `take` methods:
+You may use the `skip` and `take` methods to limit the number of results returned from the query or to skip a given number of results in the query:
$users = DB::table('users')->skip(10)->take(5)->get();
-Alternatively, you may use the `limit` and `offset` methods:
+Alternatively, you may use the `limit` and `offset` methods. These methods are functionally equivalent to the `take` and `skip` methods, respectively:
$users = DB::table('users')
->offset(10)
@@ -671,7 +726,7 @@ Alternatively, you may use the `limit` and `offset` methods:
## Conditional Clauses
-Sometimes you may want clauses to apply to a query only when something else is true. For instance you may only want to apply a `where` statement if a given input value is present on the incoming request. You may accomplish this using the `when` method:
+Sometimes you may want certain query clauses to apply to a query based on another condition. For instance, you may only want to apply a `where` statement if a given input value is present on the incoming HTTP request. You may accomplish this using the `when` method:
$role = $request->input('role');
@@ -681,52 +736,44 @@ Sometimes you may want clauses to apply to a query only when something else is t
})
->get();
-The `when` method only executes the given Closure when the first parameter is `true`. If the first parameter is `false`, the Closure will not be executed.
+The `when` method only executes the given closure when the first argument is `true`. If the first argument is `false`, the closure will not be executed. So, in the example above, the closure given to the `when` method will only be invoked if the `role` field is present on the incoming request and evaluates to `true`.
-You may pass another Closure as the third parameter to the `when` method. This Closure will execute if the first parameter evaluates as `false`. To illustrate how this feature may be used, we will use it to configure the default sorting of a query:
+You may pass another closure as the third argument to the `when` method. This closure will only execute if the first argument evaluates as `false`. To illustrate how this feature may be used, we will use it to configure the default ordering of a query:
- $sortBy = null;
+ $sortByVotes = $request->input('sort_by_votes');
$users = DB::table('users')
- ->when($sortBy, function ($query, $sortBy) {
- return $query->orderBy($sortBy);
+ ->when($sortByVotes, function ($query, $sortByVotes) {
+ return $query->orderBy('votes');
}, function ($query) {
return $query->orderBy('name');
})
->get();
-
-## Inserts
+
+## Insert Statements
-The query builder also provides an `insert` method for inserting records into the database table. The `insert` method accepts an array of column names and values:
+The query builder also provides an `insert` method that may be used to insert records into the database table. The `insert` method accepts an array of column names and values:
- DB::table('users')->insert(
- ['email' => 'john@example.com', 'votes' => 0]
- );
+ DB::table('users')->insert([
+ 'email' => 'kayla@example.com',
+ 'votes' => 0
+ ]);
-You may even insert several records into the table with a single call to `insert` by passing an array of arrays. Each array represents a row to be inserted into the table:
+You may insert several records at once by passing an array of arrays. Each array represents a record that should be inserted into the table:
DB::table('users')->insert([
- ['email' => 'taylor@example.com', 'votes' => 0],
- ['email' => 'dayle@example.com', 'votes' => 0],
+ ['email' => 'picard@example.com', 'votes' => 0],
+ ['email' => 'janeway@example.com', 'votes' => 0],
]);
The `insertOrIgnore` method will ignore duplicate record errors while inserting records into the database:
DB::table('users')->insertOrIgnore([
- ['id' => 1, 'email' => 'taylor@example.com'],
- ['id' => 2, 'email' => 'dayle@example.com'],
+ ['id' => 1, 'email' => 'sisko@example.com'],
+ ['id' => 2, 'email' => 'archer@example.com'],
]);
-The `upsert` method will insert rows that do not exist and update the rows that already exist with the new values. The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table. The method's third and final argument is an array of columns that should be updated if a matching record already exists in the database:
-
- DB::table('flights')->upsert([
- ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
- ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
- ], ['departure', 'destination'], ['price']);
-
-> {note} All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index.
-
#### Auto-Incrementing IDs
@@ -738,10 +785,24 @@ If the table has an auto-incrementing id, use the `insertGetId` method to insert
> {note} When using PostgreSQL the `insertGetId` method expects the auto-incrementing column to be named `id`. If you would like to retrieve the ID from a different "sequence", you may pass the column name as the second parameter to the `insertGetId` method.
-
-## Updates
+
+### Upserts
+
+The `upsert` method will insert records that do not exist and update the records that already exist with new values that you may specify. The method's first argument consists of the values to insert or update, while the second argument lists the column(s) that uniquely identify records within the associated table. The method's third and final argument is an array of columns that should be updated if a matching record already exists in the database:
-In addition to inserting records into the database, the query builder can also update existing records using the `update` method. The `update` method, like the `insert` method, accepts an array of column and value pairs containing the columns to be updated. You may constrain the `update` query using `where` clauses:
+ DB::table('flights')->upsert([
+ ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
+ ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
+ ], ['departure', 'destination'], ['price']);
+
+In the example above, Laravel will attempt to insert two records. If a record already exists with the same `departure` and `destination` column values, Laravel will update that record's `price` column.
+
+> {note} All databases except SQL Server require the columns in the second argument of the `upsert` method to have a "primary" or "unique" index.
+
+
+## Update Statements
+
+In addition to inserting records into the database, the query builder can also update existing records using the `update` method. The `update` method, like the `insert` method, accepts an array of column and value pairs indicating the columns to be updated. You may constrain the `update` query using `where` clauses:
$affected = DB::table('users')
->where('id', 1)
@@ -750,9 +811,9 @@ In addition to inserting records into the database, the query builder can also u
#### Update Or Insert
-Sometimes you may want to update an existing record in the database or create it if no matching record exists. In this scenario, the `updateOrInsert` method may be used. The `updateOrInsert` method accepts two arguments: an array of conditions by which to find the record, and an array of column and value pairs containing the columns to be updated.
+Sometimes you may want to update an existing record in the database or create it if no matching record exists. In this scenario, the `updateOrInsert` method may be used. The `updateOrInsert` method accepts two arguments: an array of conditions by which to find the record, and an array of column and value pairs indicating the columns to be updated.
-The `updateOrInsert` method will first attempt to locate a matching database record using the first argument's column and value pairs. If the record exists, it will be updated with the values in the second argument. If the record can not be found, a new record will be inserted with the merged attributes of both arguments:
+The `updateOrInsert` method will attempt to locate a matching database record using the first argument's column and value pairs. If the record exists, it will be updated with the values in the second argument. If the record can not be found, a new record will be inserted with the merged attributes of both arguments:
DB::table('users')
->updateOrInsert(
@@ -763,7 +824,7 @@ The `updateOrInsert` method will first attempt to locate a matching database rec
### Updating JSON Columns
-When updating a JSON column, you should use `->` syntax to access the appropriate key in the JSON object. This operation is supported on MySQL 5.7+ and PostgreSQL 9.5+:
+When updating a JSON column, you should use `->` syntax to update the appropriate key in the JSON object. This operation is supported on MySQL 5.7+ and PostgreSQL 9.5+:
$affected = DB::table('users')
->where('id', 1)
@@ -772,9 +833,7 @@ When updating a JSON column, you should use `->` syntax to access the appropriat
### Increment & Decrement
-The query builder also provides convenient methods for incrementing or decrementing the value of a given column. This is a shortcut, providing a more expressive and terse interface compared to manually writing the `update` statement.
-
-Both of these methods accept at least one argument: the column to modify. A second argument may optionally be passed to control the amount by which the column should be incremented or decremented:
+The query builder also provides convenient methods for incrementing or decrementing the value of a given column. Both of these methods accept at least one argument: the column to modify. A second argument may be provided to specify the amount by which the column should be incremented or decremented:
DB::table('users')->increment('votes');
@@ -788,18 +847,16 @@ You may also specify additional columns to update during the operation:
DB::table('users')->increment('votes', 1, ['name' => 'John']);
-> {note} Model events are not fired when using the `increment` and `decrement` methods.
+
+## Delete Statements
-
-## Deletes
-
-The query builder may also be used to delete records from the table via the `delete` method. You may constrain `delete` statements by adding `where` clauses before calling the `delete` method:
+The query builder's `delete` method may be used to delete records from the table. You may constrain `delete` statements by adding "where" clauses before calling the `delete` method:
DB::table('users')->delete();
DB::table('users')->where('votes', '>', 100)->delete();
-If you wish to truncate the entire table, which will remove all rows and reset the auto-incrementing ID to zero, you may use the `truncate` method:
+If you wish to truncate an entire table, which will remove all records from the table and reset the auto-incrementing ID to zero, you may use the `truncate` method:
DB::table('users')->truncate();
@@ -811,18 +868,24 @@ When truncating a PostgreSQL database, the `CASCADE` behavior will be applied. T
## Pessimistic Locking
-The query builder also includes a few functions to help you do "pessimistic locking" on your `select` statements. To run the statement with a "shared lock", you may use the `sharedLock` method on a query. A shared lock prevents the selected rows from being modified until your transaction commits:
+The query builder also includes a few functions to help you achieve "pessimistic locking" when executing your `select` statements. To execute a statement with a "shared lock", you may call the `sharedLock` method. A shared lock prevents the selected rows from being modified until your transaction is committed:
- DB::table('users')->where('votes', '>', 100)->sharedLock()->get();
+ DB::table('users')
+ ->where('votes', '>', 100)
+ ->sharedLock()
+ ->get();
-Alternatively, you may use the `lockForUpdate` method. A "for update" lock prevents the rows from being modified or from being selected with another shared lock:
+Alternatively, you may use the `lockForUpdate` method. A "for update" lock prevents the selected records from being modified or from being selected with another shared lock:
- DB::table('users')->where('votes', '>', 100)->lockForUpdate()->get();
+ DB::table('users')
+ ->where('votes', '>', 100)
+ ->lockForUpdate()
+ ->get();
## Debugging
-You may use the `dd` or `dump` methods while building a query to dump the query bindings and SQL. The `dd` method will display the debug information and then stop executing the request. The `dump` method will display the debug information but allow the request to keep executing:
+You may use the `dd` and `dump` methods while building a query to dump the current query bindings and SQL. The `dd` method will display the debug information and then stop executing the request. The `dump` method will display the debug information but allow the request to continue executing:
DB::table('users')->where('votes', '>', 100)->dd();
diff --git a/queues.md b/queues.md
index 64304acb1a4..523282f7788 100644
--- a/queues.md
+++ b/queues.md
@@ -26,39 +26,44 @@
- [Batch Failures](#batch-failures)
- [Queueing Closures](#queueing-closures)
- [Running The Queue Worker](#running-the-queue-worker)
+ - [The `queue:work` Command](#the-queue-work-command)
- [Queue Priorities](#queue-priorities)
- [Queue Workers & Deployment](#queue-workers-and-deployment)
- [Job Expirations & Timeouts](#job-expirations-and-timeouts)
- [Supervisor Configuration](#supervisor-configuration)
- [Dealing With Failed Jobs](#dealing-with-failed-jobs)
- [Cleaning Up After Failed Jobs](#cleaning-up-after-failed-jobs)
- - [Failed Job Events](#failed-job-events)
- [Retrying Failed Jobs](#retrying-failed-jobs)
- [Ignoring Missing Models](#ignoring-missing-models)
+ - [Failed Job Events](#failed-job-events)
- [Clearing Jobs From Queues](#clearing-jobs-from-queues)
- [Job Events](#job-events)
## Introduction
-> {tip} Laravel now offers Horizon, a beautiful dashboard and configuration system for your Redis powered queues. Check out the full [Horizon documentation](/docs/{{version}}/horizon) for more information.
+While building your web application, you may have some tasks, such as parsing and storing an uploaded CSV file, that take too long to perform during a typical web request. Thankfully, Laravel allows you to easily create queued jobs that may be processed in the background. By moving time intensive tasks to a queue, your application can respond to web requests with blazing speed and provide a better user experience to your customers.
+
+Laravel queues provide a unified queueing API across a variety of different queue backends, such as [Amazon SQS](https://aws.amazon.com/sqs/), [Redis](https://redis.io), or even a relational database.
-Laravel queues provide a unified API across a variety of different queue backends, such as Beanstalk, Amazon SQS, Redis, or even a relational database. Queues allow you to defer the processing of a time consuming task, such as sending an email, until a later time. Deferring these time consuming tasks drastically speeds up web requests to your application.
+Laravel's queue configuration options are stored in your application's `config/queue.php` configuration file. In this file you will find connection configurations for each of the queue drivers that are included with the framework, including the database, [Amazon SQS](https://aws.amazon.com/sqs/), [Redis](https://redis.io), and [Beanstalkd](https://beanstalkd.github.io/) drivers, as well as a synchronous driver that will execute jobs immediately (for use during local development). A `null` queue driver is also included which discards queued jobs.
-The queue configuration file is stored in `config/queue.php`. In this file you will find connection configurations for each of the queue drivers that are included with the framework, which includes a database, [Beanstalkd](https://beanstalkd.github.io/), [Amazon SQS](https://aws.amazon.com/sqs/), [Redis](https://redis.io), and a synchronous driver that will execute jobs immediately (for local use). A `null` queue driver is also included which discards queued jobs.
+> {tip} Laravel now offers Horizon, a beautiful dashboard and configuration system for your Redis powered queues. Check out the full [Horizon documentation](/docs/{{version}}/horizon) for more information.
### Connections Vs. Queues
-Before getting started with Laravel queues, it is important to understand the distinction between "connections" and "queues". In your `config/queue.php` configuration file, there is a `connections` configuration option. This option defines a particular connection to a backend service such as Amazon SQS, Beanstalk, or Redis. However, any given queue connection may have multiple "queues" which may be thought of as different stacks or piles of queued jobs.
+Before getting started with Laravel queues, it is important to understand the distinction between "connections" and "queues". In your `config/queue.php` configuration file, there is a `connections` configuration array. This option defines the connections to backend queue services such as Amazon SQS, Beanstalk, or Redis. However, any given queue connection may have multiple "queues" which may be thought of as different stacks or piles of queued jobs.
Note that each connection configuration example in the `queue` configuration file contains a `queue` attribute. This is the default queue that jobs will be dispatched to when they are sent to a given connection. In other words, if you dispatch a job without explicitly defining which queue it should be dispatched to, the job will be placed on the queue that is defined in the `queue` attribute of the connection configuration:
- // This job is sent to the default queue...
- Job::dispatch();
+ use App\Jobs\ProcessPodcast;
+
+ // This job is sent to the default connection's default queue...
+ ProcessPodcast::dispatch();
- // This job is sent to the "emails" queue...
- Job::dispatch()->onQueue('emails');
+ // This job is sent to the default connection's "emails" queue...
+ ProcessPodcast::dispatch()->onQueue('emails');
Some applications may not need to ever push jobs onto multiple queues, instead preferring to have one simple queue. However, pushing jobs to multiple queues can be especially useful for applications that wish to prioritize or segment how jobs are processed, since the Laravel queue worker allows you to specify which queues it should process by priority. For example, if you push jobs to a `high` queue, you may run a worker that gives them higher processing priority:
@@ -111,7 +116,7 @@ Adjusting this value based on your queue load can be more efficient than continu
#### Other Driver Prerequisites
-The following dependencies are needed for the listed queue drivers:
+The following dependencies are needed for the listed queue drivers. These dependencies may be installed via the Composer package manager:
- Amazon SQS: `aws/aws-sdk-php ~3.0`
@@ -125,7 +130,7 @@ The following dependencies are needed for the listed queue drivers:
### Generating Job Classes
-By default, all of the queueable jobs for your application are stored in the `app/Jobs` directory. If the `app/Jobs` directory doesn't exist, it will be created when you run the `make:job` Artisan command. You may generate a new queued job using the Artisan CLI:
+By default, all of the queueable jobs for your application are stored in the `app/Jobs` directory. If the `app/Jobs` directory doesn't exist, it will be created when you run the `make:job` Artisan command:
php artisan make:job ProcessPodcast
@@ -136,7 +141,7 @@ The generated class will implement the `Illuminate\Contracts\Queue\ShouldQueue`
### Class Structure
-Job classes are very simple, normally containing only a `handle` method which is called when the job is processed by the queue. To get started, let's take a look at an example job class. In this example, we'll pretend we manage a podcast publishing service and need to process the uploaded podcast files before they are published:
+Job classes are very simple, normally containing only a `handle` method that is invoked when the job is processed by the queue. To get started, let's take a look at an example job class. In this example, we'll pretend we manage a podcast publishing service and need to process the uploaded podcast files before they are published:
+#### `handle` Method Dependency Injection
-The `handle` method is called when the job is processed by the queue. Note that we are able to type-hint dependencies on the `handle` method of the job. The Laravel [service container](/docs/{{version}}/container) automatically injects these dependencies.
+The `handle` method is invoked when the job is processed by the queue. Note that we are able to type-hint dependencies on the `handle` method of the job. The Laravel [service container](/docs/{{version}}/container) automatically injects these dependencies.
-If you would like to take total control over how the container injects dependencies into the `handle` method, you may use the container's `bindMethod` method. The `bindMethod` method accepts a callback which receives the job and the container. Within the callback, you are free to invoke the `handle` method however you wish. Typically, you should call this method from a [service provider](/docs/{{version}}/providers):
+If you would like to take total control over how the container injects dependencies into the `handle` method, you may use the container's `bindMethod` method. The `bindMethod` method accepts a callback which receives the job and the container. Within the callback, you are free to invoke the `handle` method however you wish. Typically, you should call this method from the `boot` method of your `App\Providers\AppServiceProvider` [service provider](/docs/{{version}}/providers):
use App\Jobs\ProcessPodcast;
+ use App\Services\AudioProcessor;
- $this->app->bindMethod(ProcessPodcast::class.'@handle', function ($job, $app) {
+ $this->app->bindMethod([ProcessPodcast::class, 'handle'], function ($job, $app) {
return $job->handle($app->make(AudioProcessor::class));
});
@@ -196,7 +212,7 @@ If you would like to take total control over how the container injects dependenc
#### Handling Relationships
-Because loaded relationships also get serialized, the serialized job string can become quite large. To prevent relations from being serialized, you can call the `withoutRelations` method on the model when setting a property value. This method will return an instance of the model with no loaded relationships:
+Because loaded relationships also get serialized, the serialized job string can sometimes become quite large. To prevent relations from being serialized, you can call the `withoutRelations` method on the model when setting a property value. This method will return an instance of the model without its loaded relationships:
/**
* Create a new job instance.
@@ -212,9 +228,9 @@ Because loaded relationships also get serialized, the serialized job string can
### Unique Jobs
-> {note} Unique jobs require a cache driver that supports [locks](/docs/{{version}}/cache#atomic-locks).
+> {note} Unique jobs require a cache driver that supports [locks](/docs/{{version}}/cache#atomic-locks). Currently, the `memcached`, `redis`, `dynamodb`, `database`, `file`, and `array` cache drivers support atomic locks.
-Sometimes, you may want to ensure that only one instance of a specific job is on the queue at any point in time. You may do so by implementing the `ShouldBeUnique` interface on your job class:
+Sometimes, you may want to ensure that only one instance of a specific job is on the queue at any point in time. You may do so by implementing the `ShouldBeUnique` interface on your job class. This interface does not require you to define any additional methods on your class:
+#### Keeping Jobs Unique Until Processing Begins
+
+By default, unique jobs are "unlocked" after a job completes processing or fails all of its retry attempts. However, there may be situations where you would like your job to unlock immediately before it is processed. To accomplish this, your job should implement the `ShouldBeUniqueUntilProcessing` contract instead of the `ShouldBeUnique` contract:
+
+
#### Unique Job Locks
-Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts to acquire a [lock](/docs/{{version}}/cache#atomic-locks) with the `uniqueId` key. If the lock is not acquired, the dispatch is ignored. This lock is released when the job completes processing or fails all of its retry attempts. By default, Laravel will use the default cache driver to obtain this lock. However, if you wish to use another driver for acquiring the lock, you may define a `uniqueVia` method the returns the cache driver that should be used:
+Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts to acquire a [lock](/docs/{{version}}/cache#atomic-locks) with the `uniqueId` key. If the lock is not acquired, the job is not dispatched. This lock is released when the job completes processing or fails all of its retry attempts. By default, Laravel will use the default cache driver to obtain this lock. However, if you wish to use another driver for acquiring the lock, you may define a `uniqueVia` method the returns the cache driver that should be used:
use Illuminate\Support\Facades\Cache;
@@ -294,6 +326,8 @@ Behind the scenes, when a `ShouldBeUnique` job is dispatched, Laravel attempts t
Job middleware allow you to wrap custom logic around the execution of queued jobs, reducing boilerplate in the jobs themselves. For example, consider the following `handle` method which leverages Laravel's Redis rate limiting features to allow only one job to process every five seconds:
+ use Illuminate\Support\Facades\Redis;
+
/**
* Execute the job.
*
@@ -312,7 +346,7 @@ Job middleware allow you to wrap custom logic around the execution of queued job
});
}
-While this code is valid, the structure of the `handle` method becomes noisy since it is cluttered with Redis rate limiting logic. In addition, this rate limiting logic must be duplicated for any other jobs that we want to rate limit.
+While this code is valid, the implementation of the `handle` method becomes noisy since it is cluttered with Redis rate limiting logic. In addition, this rate limiting logic must be duplicated for any other jobs that we want to rate limit.
Instead of rate limiting in the handle method, we could define a job middleware that handles rate limiting. Laravel does not have a default location for job middleware, so you are welcome to place job middleware anywhere in your application. In this example, we will place the middleware in a `app/Jobs/Middleware` directory:
@@ -349,7 +383,7 @@ Instead of rate limiting in the handle method, we could define a job middleware
As you can see, like [route middleware](/docs/{{version}}/middleware), job middleware receive the job being processed and a callback that should be invoked to continue processing the job.
-After creating job middleware, they may be attached to a job by returning them from the job's `middleware` method. This method does not exist on jobs scaffolded by the `make:job` Artisan command, so you will need to add it to your own job class definition:
+After creating job middleware, they may be attached to a job by returning them from the job's `middleware` method. This method does not exist on jobs scaffolded by the `make:job` Artisan command, so you will need to manually add it to your job class:
use App\Jobs\Middleware\RateLimited;
@@ -366,20 +400,32 @@ After creating job middleware, they may be attached to a job by returning them f
### Rate Limiting
-Although we just demonstrated how to write your own rate limiting job middleware, Laravel includes a rate limiting middleware that you may utilize to rate limit jobs. Like [route rate limiters](/docs/{{version}}/routing#defining-rate-limiter), job rate limiters are defined using the `RateLimiter` facade's `for` method.
+Although we just demonstrated how to write your own rate limiting job middleware, Laravel actually includes a rate limiting middleware that you may utilize to rate limit jobs. Like [route rate limiters](/docs/{{version}}/routing#defining-rate-limiter), job rate limiters are defined using the `RateLimiter` facade's `for` method.
-For example, you may wish to allow users to backup their data once per hour while imposing no such limit on premium customers. To accomplish this, you may define a `RateLimiter` in your `AppServiceProvider`:
+For example, you may wish to allow users to backup their data once per hour while imposing no such limit on premium customers. To accomplish this, you may define a `RateLimiter` in the `boot` method of your `AppServiceProvider`:
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
- RateLimiter::for('backups', function ($job) {
- return $job->user->vipCustomer()
- ? Limit::none()
- : Limit::perHour(1)->by($job->user->id);
- });
+ /**
+ * Bootstrap any application services.
+ *
+ * @return void
+ */
+ public function boot()
+ {
+ RateLimiter::for('backups', function ($job) {
+ return $job->user->vipCustomer()
+ ? Limit::none()
+ : Limit::perHour(1)->by($job->user->id);
+ });
+ }
+
+In the example above, we defined an hourly rate limit; however, you may easily define a rate limit based on minutes using the `perMinute` method. In addition, you may pass any value you wish to the `by` method of the rate limit; however, this value is most often used to segment rate limits by customer:
+
+ return Limit::perMinute(50)->by($job->user->id);
-You may then attach the rate limiter to your backup job using the `RateLimited` middleware. Each time the job exceeds the rate limit, this middleware will release the job back to the queue with an appropriate delay based on the rate limit duration.
+Once you have defined your rate limit, you may attach the rate limiter to your backup job using the `Illuminate\Queue\Middleware\RateLimited` middleware. Each time the job exceeds the rate limit, this middleware will release the job back to the queue with an appropriate delay based on the rate limit duration.
use Illuminate\Queue\Middleware\RateLimited;
@@ -395,14 +441,14 @@ You may then attach the rate limiter to your backup job using the `RateLimited`
Releasing a rate limited job back onto the queue will still increment the job's total number of `attempts`. You may wish to tune your `tries` and `maxExceptions` properties on your job class accordingly. Or, you may wish to use the [`retryUntil` method](#time-based-attempts) to define the amount of time until the job should no longer be attempted.
-> {tip} If you are using Redis, you may use the `RateLimitedWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic rate limiting middleware.
+> {tip} If you are using Redis, you may use the `Illuminate\Queue\Middleware\RateLimitedWithRedis` middleware, which is fine-tuned for Redis and more efficient than the basic rate limiting middleware.
### Preventing Job Overlaps
-Laravel includes a `WithoutOverlapping` middleware that allows you to prevent job overlaps based on an arbitrary key. This can be helpful when a queued job is modifying a resource that should only be modified by one job at a time.
+Laravel includes an `Illuminate\Queue\Middleware\WithoutOverlapping` middleware that allows you to prevent job overlaps based on an arbitrary key. This can be helpful when a queued job is modifying a resource that should only be modified by one job at a time.
-For example, let's imagine you have a refund processing job and you want to prevent refund job overlaps for the same order ID. To accomplish this, you can return the `WithoutOverlapping` middleware from your refund processing job's `middleware` method:
+For example, let's imagine you have a queued job that updates a user's credit score and you want to prevent credit score update job overlaps for the same user ID. To accomplish this, you can return the `WithoutOverlapping` middleware from your job's `middleware` method:
use Illuminate\Queue\Middleware\WithoutOverlapping;
@@ -413,10 +459,10 @@ For example, let's imagine you have a refund processing job and you want to prev
*/
public function middleware()
{
- return [new WithoutOverlapping($this->order->id)];
+ return [new WithoutOverlapping($this->user->id)];
}
-Any overlapping jobs will be released back to the queue. You may also specify the number of seconds that must elapse before the job will be attempted again:
+Any overlapping jobs will be released back to the queue. You may also specify the number of seconds that must elapse before the released job will be attempted again:
/**
* Get the middleware the job should pass through.
@@ -428,7 +474,7 @@ Any overlapping jobs will be released back to the queue. You may also specify th
return [(new WithoutOverlapping($this->order->id))->releaseAfter(60)];
}
-If you wish to immediately delete any overlapping jobs, you may use the `dontRelease` method:
+If you wish to immediately delete any overlapping jobs so that they will not be retried, you may use the `dontRelease` method:
/**
* Get the middleware the job should pass through.
@@ -440,7 +486,7 @@ If you wish to immediately delete any overlapping jobs, you may use the `dontRel
return [(new WithoutOverlapping($this->order->id))->dontRelease()];
}
-> {note} The `WithoutOverlapping` middleware requires a cache driver that supports [locks](/docs/{{version}}/cache#atomic-locks).
+> {note} The `WithoutOverlapping` middleware requires a cache driver that supports [locks](/docs/{{version}}/cache#atomic-locks). Currently, the `memcached`, `redis`, `dynamodb`, `database`, `file`, and `array` cache drivers support atomic locks.
## Dispatching Jobs
@@ -453,6 +499,7 @@ Once you have written your job class, you may dispatch it using the `dispatch` m
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
+ use App\Models\Podcast;
use Illuminate\Http\Request;
class PodcastController extends Controller
@@ -460,12 +507,14 @@ Once you have written your job class, you may dispatch it using the `dispatch` m
/**
* Store a new podcast.
*
- * @param Request $request
- * @return Response
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
- // Create podcast...
+ $podcast = Podcast::create(...);
+
+ // ...
ProcessPodcast::dispatch($podcast);
}
@@ -473,14 +522,14 @@ Once you have written your job class, you may dispatch it using the `dispatch` m
If you would like to conditionally dispatch a job, you may use the `dispatchIf` and `dispatchUnless` methods:
- ProcessPodcast::dispatchIf($accountActive === true, $podcast);
+ ProcessPodcast::dispatchIf($accountActive, $podcast);
- ProcessPodcast::dispatchUnless($accountSuspended === false, $podcast);
+ ProcessPodcast::dispatchUnless($accountSuspended, $podcast);
### Delayed Dispatching
-If you would like to delay the execution of a queued job, you may use the `delay` method when dispatching a job. For example, let's specify that a job should not be available for processing until 10 minutes after it has been dispatched:
+If you would like to specify that a job should not be immediately available for processing by a queue worker, you may use the `delay` method when dispatching the job. For example, let's specify that a job should not be available for processing until 10 minutes after it has been dispatched:
delay(now()->addMinutes(10));
+ ->delay(now()->addMinutes(10));
}
}
@@ -512,25 +564,25 @@ If you would like to delay the execution of a queued job, you may use the `delay
#### Dispatching After The Response Is Sent To Browser
-Alternatively, the `dispatchAfterResponse` method delays dispatching a job until after the response is sent to the user's browser. This will still allow the user to begin using the application even though a queued job is still executing. This should typically only be used for jobs that take about a second, such as sending an email:
+Alternatively, the `dispatchAfterResponse` method delays dispatching a job until after the HTTP response is sent to the user's browser. This will still allow the user to begin using the application even though a queued job is still executing. This should typically only be used for jobs that take about a second, such as sending an email. Since they are processed within the current HTTP request, jobs dispatched in this fashion do not require a queue worker to be running in order for them to be processed:
use App\Jobs\SendNotification;
SendNotification::dispatchAfterResponse();
-You may `dispatch` a Closure and chain the `afterResponse` method onto the helper to execute a Closure after the response has been sent to the browser:
+You may also `dispatch` a closure and chain the `afterResponse` method onto the `dispatch` helper to execute a closure after the HTTP response has been sent to the browser:
use App\Mail\WelcomeMessage;
use Illuminate\Support\Facades\Mail;
dispatch(function () {
- Mail::to('taylor@laravel.com')->send(new WelcomeMessage);
+ Mail::to('taylor@example.com')->send(new WelcomeMessage);
})->afterResponse();
### Synchronous Dispatching
-If you would like to dispatch a job immediately (synchronously), you may use the `dispatchSync` method. When using this method, the job will not be queued and will be run immediately within the current process:
+If you would like to dispatch a job immediately (synchronously), you may use the `dispatchSync` method. When using this method, the job will not be queued and will be executed immediately within the current process:
### Job Chaining
-Job chaining allows you to specify a list of queued jobs that should be run in sequence after the primary job has executed successfully. If one job in the sequence fails, the rest of the jobs will not be run. To execute a queued job chain, you may use the `chain` method provided by the `Bus` facade:
+Job chaining allows you to specify a list of queued jobs that should be run in sequence after the primary job has executed successfully. If one job in the sequence fails, the rest of the jobs will not be run. To execute a queued job chain, you may use the `chain` method provided by the `Bus` facade. Laravel's command bus is a lower level component that queued job dispatching is built on top of:
+ use App\Jobs\OptimizePodcast;
+ use App\Jobs\ProcessPodcast;
+ use App\Jobs\ReleasePodcast;
use Illuminate\Support\Facades\Bus;
Bus::chain([
@@ -569,7 +627,7 @@ Job chaining allows you to specify a list of queued jobs that should be run in s
new ReleasePodcast,
])->dispatch();
-In addition to chaining job class instances, you may also chain Closures:
+In addition to chaining job class instances, you may also chain closures:
Bus::chain([
new ProcessPodcast,
@@ -579,7 +637,7 @@ In addition to chaining job class instances, you may also chain Closures:
},
])->dispatch();
-> {note} Deleting jobs using the `$this->delete()` method will not prevent chained jobs from being processed. The chain will only stop executing if a job in the chain fails.
+> {note} Deleting jobs using the `$this->delete()` method within the job will not prevent chained jobs from being processed. The chain will only stop executing if a job in the chain fails.
#### Chain Connection & Queue
@@ -595,7 +653,7 @@ If you would like to specify the connection and queue that should be used for th
#### Chain Failures
-When chaining jobs, you may use the `catch` method to specify a Closure that should be invoked if a job within the chain fails. The given callback will receive the exception instance that caused the job failure:
+When chaining jobs, you may use the `catch` method to specify a closure that should be invoked if a job within the chain fails. The given callback will receive the `Throwable` instance that caused the job failure:
use Illuminate\Support\Facades\Bus;
use Throwable;
@@ -622,6 +680,7 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e
use App\Http\Controllers\Controller;
use App\Jobs\ProcessPodcast;
+ use App\Models\Podcast;
use Illuminate\Http\Request;
class PodcastController extends Controller
@@ -629,21 +688,50 @@ By pushing jobs to different queues, you may "categorize" your queued jobs and e
/**
* Store a new podcast.
*
- * @param Request $request
- * @return Response
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
+ $podcast = Podcast::create(...);
+
// Create podcast...
ProcessPodcast::dispatch($podcast)->onQueue('processing');
}
}
+Alternatively, you may specify the job's queue by calling the `onQueue` method within the job's constructor:
+
+ onQueue('processing');
+ }
+ }
+
#### Dispatching To A Particular Connection
-If you are working with multiple queue connections, you may specify which connection to push a job to. To specify the connection, use the `onConnection` method when dispatching the job:
+If your application interacts with multiple queue connections, you may specify which connection to push a job to using the `onConnection` method:
onConnection('sqs');
}
}
-You may chain the `onConnection` and `onQueue` methods to specify the connection and the queue for a job:
+You may chain the `onConnection` and `onQueue` methods together to specify the connection and the queue for a job:
ProcessPodcast::dispatch($podcast)
->onConnection('sqs')
->onQueue('processing');
+Alternatively, you may specify the job's connection by calling the `onConnection` method within the job's constructor:
+
+ onConnection('sqs');
+ }
+ }
+
### Specifying Max Job Attempts / Timeout Values
#### Max Attempts
-One approach to specifying the maximum number of times a job may be attempted is via the `--tries` switch on the Artisan command line:
+If one of your queued jobs is encountering an error, you likely do not want it to keep retrying indefinitely. Therefore, Laravel provides various ways to specify how many times or for how long a job may be attempted.
+
+One approach to specifying the maximum number of times a job may be attempted is via the `--tries` switch on the Artisan command line. This will apply to all jobs processed by the worker unless the job being processed specifies a more specific number of times it may be attempted:
php artisan queue:work --tries=3
-However, you may take a more granular approach by defining the maximum number of attempts on the job class itself. If the maximum number of attempts is specified on the job, it will take precedence over the value provided on the command line:
+If a job exceeds its maximum number of attempts, it will be considered a "failed" job. For more information on handling failed jobs, consult the [failed job documentation](#dealing-with-failed-jobs).
+
+You may take a more granular approach by defining the maximum number of times a job may be attempted on the job class itself. If the maximum number of attempts is specified on the job, it will take precedence over the `--tries` value provided on the command line:
#### Time Based Attempts
-As an alternative to defining how many times a job may be attempted before it fails, you may define a time at which the job should timeout. This allows a job to be attempted any number of times within a given time frame. To define the time at which a job should timeout, add a `retryUntil` method to your job class:
+As an alternative to defining how many times a job may be attempted before it fails, you may define a time at which the job should no longer be attempted. This allows a job to be attempted any number of times within a given time frame. To define the time at which a job should no longer be attempted, add a `retryUntil` method to your job class. This method should return a `DateTime` instance:
/**
* Determine the time at which the job should timeout.
@@ -713,20 +835,22 @@ As an alternative to defining how many times a job may be attempted before it fa
*/
public function retryUntil()
{
- return now()->addSeconds(5);
+ return now()->addMinutes(10);
}
-> {tip} You may also define a `retryUntil` method on your queued event listeners.
+> {tip} You may also define a `tries` property or `retryUntil` method on your [queued event listeners](/docs/{{version}}/events#queued-event-listeners).
#### Max Exceptions
-Sometimes you may wish to specify that a job may be attempted many times, but should fail if the retries are triggered by a given number of exceptions. To accomplish this, you may define a `maxExceptions` property on your job class:
+Sometimes you may wish to specify that a job may be attempted many times, but should fail if the retries are triggered by a given number of unhandled exceptions (as opposed to being released by the `release` method directly). To accomplish this, you may define a `maxExceptions` property on your job class:
{note} The `pcntl` PHP extension must be installed in order to specify job timeouts.
-Likewise, the maximum number of seconds that jobs can run may be specified using the `--timeout` switch on the Artisan command line:
+Often, you know roughly how long you expect your queued jobs to take. For this reason, Laravel allows you to specify a "timeout" value. If a job is processing for longer than the number of seconds specified by the timeout value, the worker processing the job will exit with an error. Typically, the worker will be restarted automatically by a [process manager configured on your server](#supervisor-configuration).
+
+The maximum number of seconds that jobs can run may be specified using the `--timeout` switch on the Artisan command line:
php artisan queue:work --timeout=30
-However, you may also define the maximum number of seconds a job should be allowed to run on the job class itself. If the timeout is specified on the job, it will take precedence over any timeout specified on the command line:
+If the job exceeds its maximum attempts by continually timing out, it will be marked as failed.
+
+You may also define the maximum number of seconds a job should be allowed to run on the job class itself. If the timeout is specified on the job, it will take precedence over any timeout specified on the command line:
+#### Manually Releasing A Job
+
+Sometimes you may wish to manually release a job back onto the queue so that it can be attempted again at a later time. You may accomplish this by calling the `release` method:
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ // ...
+
+ $this->release();
+ }
+
+By default, the `release` method will release the job back onto the queue for immediate processing. However, by passing an integer to the `release` method you may instruct the queue to not make the job available for processing until a given number of seconds has elapsed:
+
+ $this->release(10)
+
+
+#### Manually Failing A Job
+
+Occasionally you may need to manually mark a job as "failed". To do so, you may call the `fail` method:
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ // ...
+
+ $this->fail();
+ }
+
+If you would like to mark your job as failed because of an exception that you have caught, you may pass the exception to the `fail` method:
+
+ $this->fail($exception);
+
+> {tip} For more information on failed jobs, check out the [documentation on dealing with job failures](#dealing-with-failed-jobs).
+
## Job Batching
-Laravel's job batching feature allows you to easily execute a batch of jobs and then perform some action when the batch of jobs has completed executing. Before getting started, you should create a database migration to build a table that will contain your job batch meta information. This migration may be generated using the `queue:batches-table` Artisan command:
+Laravel's job batching feature allows you to easily execute a batch of jobs and then perform some action when the batch of jobs has completed executing. Before getting started, you should create a database migration to build a table to contain meta information about your job batches, such as their completion percentage. This migration may be generated using the `queue:batches-table` Artisan command:
php artisan queue:batches-table
@@ -805,14 +977,12 @@ Laravel's job batching feature allows you to easily execute a batch of jobs and
### Defining Batchable Jobs
-To build a batchable job, you should [create a queueable job](#creating-jobs) as normal; however, you should add the `Illuminate\Bus\Batchable` trait to the job class. This trait provides access to a `batch` method which may be used to retrieve the current batch that the job is executing in:
+To define a batchable job, you should [create a queueable job](#creating-jobs) as normal; however, you should add the `Illuminate\Bus\Batchable` trait to the job class. This trait provides access to a `batch` method which may be used to retrieve the current batch that the job is executing within:
batch()->cancelled()) {
- // Detected cancelled batch...
+ // Determine if the batch has been cancelled...
return;
}
- // Batched job executing...
+ // Import a portion of the CSV file...
}
}
### Dispatching Batches
-To dispatch a batch of jobs, you should use the `batch` method of the `Bus` facade. Of course, batching is primarily useful when combined with completion callbacks. So, you may use the `then`, `catch`, and `finally` methods to define completion callbacks for the batch. Each of these callbacks will receive an `Illuminate\Bus\Batch` instance when they are invoked:
+To dispatch a batch of jobs, you should use the `batch` method of the `Bus` facade. Of course, batching is primarily useful when combined with completion callbacks. So, you may use the `then`, `catch`, and `finally` methods to define completion callbacks for the batch. Each of these callbacks will receive an `Illuminate\Bus\Batch` instance when they are invoked. In this example, we will imagine we are queueing a batch of jobs that each process a given number of rows from a CSV file:
- use App\Jobs\ProcessPodcast;
- use App\Podcast;
+ use App\Jobs\ImportCsv;
use Illuminate\Bus\Batch;
use Illuminate\Support\Facades\Bus;
use Throwable;
$batch = Bus::batch([
- new ProcessPodcast(Podcast::find(1)),
- new ProcessPodcast(Podcast::find(2)),
- new ProcessPodcast(Podcast::find(3)),
- new ProcessPodcast(Podcast::find(4)),
- new ProcessPodcast(Podcast::find(5)),
+ new ImportCsv(1, 100),
+ new ImportCsv(101, 200),
+ new ImportCsv(201, 300),
+ new ImportCsv(301, 400),
+ new ImportCsv(401, 500),
])->then(function (Batch $batch) {
// All jobs completed successfully...
})->catch(function (Batch $batch, Throwable $e) {
@@ -868,6 +1037,8 @@ To dispatch a batch of jobs, you should use the `batch` method of the `Bus` faca
return $batch->id;
+The batch's ID, which may be accessed via the `$batch->id` property, may be used to [query the Laravel command bus](#inspecting-batches) for information about the batch after it has been dispatched.
+
#### Naming Batches
@@ -877,23 +1048,28 @@ Some tools such as Laravel Horizon and Laravel Telescope may provide more user-f
// ...
])->then(function (Batch $batch) {
// All jobs completed successfully...
- })->name('Process Podcasts')->dispatch();
+ })->name('Import CSV')->dispatch();
#### Batch Connection & Queue
-If you would like to specify the connection and queue that should be used for the batched jobs, you may use the `onConnection` and `onQueue` methods:
+If you would like to specify the connection and queue that should be used for the batched jobs, you may use the `onConnection` and `onQueue` methods. All batched jobs must execute within the same connection and queue:
$batch = Bus::batch([
// ...
])->then(function (Batch $batch) {
// All jobs completed successfully...
- })->onConnection('redis')->onQueue('podcasts')->dispatch();
+ })->onConnection('redis')->onQueue('imports')->dispatch();
#### Chains Within Batches
-You may add a set of [chained jobs](#job-chaining) within a batch by placing the chained jobs within an array. For example, we may execute two job chains in parallel. Since the two chains are batched, we will be able to inspect the batch completion progress as a whole:
+You may define a set of [chained jobs](#job-chaining) within a batch by placing the chained jobs within an array. For example, we may execute two job chains in parallel and execute a callback when both job chains have finished processing:
+
+ use App\Jobs\ReleasePodcast;
+ use App\Jobs\SendPodcastReleaseNotification;
+ use Illuminate\Bus\Batch;
+ use Illuminate\Support\Facades\Bus;
Bus::batch([
[
@@ -904,12 +1080,14 @@ You may add a set of [chained jobs](#job-chaining) within a batch by placing the
new ReleasePodcast(2),
new SendPodcastReleaseNotification(2),
],
- ])->dispatch();
+ ])->then(function (Batch $batch) {
+ // ...
+ })->dispatch();
### Adding Jobs To Batches
-Sometimes it may be useful to add additional jobs to a batch from within a batched job. This pattern can be useful when you need to batch thousands of jobs which may take too long to dispatch during a web request. So, instead, you may wish to dispatch an initial batch of "loader" jobs that hydrate the batch with more jobs:
+Sometimes it may be useful to add additional jobs to a batch from within a batched job. This pattern can be useful when you need to batch thousands of jobs which may take too long to dispatch during a web request. So, instead, you may wish to dispatch an initial batch of "loader" jobs that hydrate the batch with even more jobs:
$batch = Bus::batch([
new LoadImportBatch,
@@ -919,7 +1097,7 @@ Sometimes it may be useful to add additional jobs to a batch from within a batch
// All jobs completed successfully...
})->name('Import Contacts')->dispatch();
-In this example, we will use the `LoadImportBatch` job to hydrate the batch with additional jobs. To accomplish this, we may use the `add` method on the batch instance that can be accessed within the job:
+In this example, we will use the `LoadImportBatch` job to hydrate the batch with additional jobs. To accomplish this, we may use the `add` method on the batch instance that may be accessed via the job's `batch` method:
use App\Jobs\ImportContacts;
use Illuminate\Support\Collection;
@@ -945,7 +1123,7 @@ In this example, we will use the `LoadImportBatch` job to hydrate the batch with
### Inspecting Batches
-The `Illuminate\Bus\Batch` method that is provided to batch completion callbacks has a variety of properties and methods to assist you in interacting with and inspecting a given batch of jobs.
+The `Illuminate\Bus\Batch` instance that is provided to batch completion callbacks has a variety of properties and methods to assist you in interacting with and inspecting a given batch of jobs:
// The UUID of the batch...
$batch->id;
@@ -980,7 +1158,9 @@ The `Illuminate\Bus\Batch` method that is provided to batch completion callbacks
#### Returning Batches From Routes
-All `Illuminate\Bus\Batch` instances are JSON serializable, meaning you can return them directly from one of your application's routes to retrieve a JSON payload containing information about the batch, including its completion progress. To retrieve a batch by its ID, you may use the `Bus` facade's `findBatch` method:
+All `Illuminate\Bus\Batch` instances are JSON serializable, meaning you can return them directly from one of your application's routes to retrieve a JSON payload containing information about the batch, including its completion progress. This makes it convenient to display information about the batch's completion progress in your application's UI.
+
+To retrieve a batch by its ID, you may use the `Bus` facade's `findBatch` method:
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Route;
@@ -1010,10 +1190,26 @@ Sometimes you may need to cancel a given batch's execution. This can be accompli
}
}
+As you may have noticed in previous examples, batched jobs should typically check to see if the batch has been cancelled at the beginning of their `handle` method:
+
+ /**
+ * Execute the job.
+ *
+ * @return void
+ */
+ public function handle()
+ {
+ if ($this->batch()->cancelled()) {
+ return;
+ }
+
+ // Continue processing...
+ }
+
### Batch Failures
-When a batch job fails, the `catch` callback (if assigned) will be invoked. This callback is only invoked for the job that fails within the batch.
+When a batched job fails, the `catch` callback (if assigned) will be invoked. This callback is only invoked for the first job that fails within the batch.
#### Allowing Failures
@@ -1031,12 +1227,14 @@ When a job within a batch fails, Laravel will automatically mark the batch as "c
For convenience, Laravel provides a `queue:retry-batch` Artisan command that allows you to easily retry all of the failed jobs for a given batch. The `queue:retry-batch` command accepts the UUID of the batch whose failed jobs should be retried:
- php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5
+```bash
+php artisan queue:retry-batch 32dbc76c-4f82-4749-b610-a639fe0099b5
+```
## Queueing Closures
-Instead of dispatching a job class to the queue, you may also dispatch a Closure. This is great for quick, simple tasks that need to be executed outside of the current request cycle. When dispatching Closures to the queue, the Closure's code contents is cryptographically signed so it can not be modified in transit:
+Instead of dispatching a job class to the queue, you may also dispatch a closure. This is great for quick, simple tasks that need to be executed outside of the current request cycle. When dispatching closures to the queue, the closure's code content is cryptographically signed so that it can not be modified in transit:
$podcast = App\Podcast::find(1);
@@ -1044,7 +1242,7 @@ Instead of dispatching a job class to the queue, you may also dispatch a Closure
$podcast->publish();
});
-Using the `catch` method, you may provide a Closure that should be executed if the queued Closure fails to complete successfully after exhausting all of your queue's configured retry attempts:
+Using the `catch` method, you may provide a closure that should be executed if the queued closure fails to complete successfully after exhausting all of your queue's [configured retry attempts](#max-job-attempts-and-timeout):
use Throwable;
@@ -1057,7 +1255,10 @@ Using the `catch` method, you may provide a Closure that should be executed if t
## Running The Queue Worker
-Laravel includes a queue worker that will process new jobs as they are pushed onto the queue. You may run the worker using the `queue:work` Artisan command. Note that once the `queue:work` command has started, it will continue to run until it is manually stopped or you close your terminal:
+
+### The `queue:work` Command
+
+Laravel includes an Artisan command that will start a queue worker and process new jobs as they are pushed onto the queue. You may run the worker using the `queue:work` Artisan command. Note that once the `queue:work` command has started, it will continue to run until it is manually stopped or you close your terminal:
php artisan queue:work
@@ -1065,10 +1266,15 @@ Laravel includes a queue worker that will process new jobs as they are pushed on
Remember, queue workers are long-lived processes and store the booted application state in memory. As a result, they will not notice changes in your code base after they have been started. So, during your deployment process, be sure to [restart your queue workers](#queue-workers-and-deployment). In addition, remember that any static state created or modified by your application will not be automatically reset between jobs.
-Alternatively, you may run the `queue:listen` command. When using the `queue:listen` command, you don't have to manually restart the worker when you want to reload your updated code or reset the application state; however, this command is not as efficient as `queue:work`:
+Alternatively, you may run the `queue:listen` command. When using the `queue:listen` command, you don't have to manually restart the worker when you want to reload your updated code or reset the application state; however, this command is significantly less efficient than the `queue:work` command:
php artisan queue:listen
+
+#### Running Multiple Queue Workers
+
+To assign multiple workers to a queue and process jobs concurrently, you should simply start multiple `queue:work` processes. This can either be done locally via multiple tabs in your terminal or in production using your process manager's configuration settings. [When using Supervisor](#supervisor-configuration), you may use the `numprocs` configuration value.
+
#### Specifying The Connection & Queue
@@ -1087,34 +1293,41 @@ The `--once` option may be used to instruct the worker to only process a single
php artisan queue:work --once
-The `--max-jobs` option may be used to instruct the worker to process the given number of jobs and then exit. This option may be useful when combined with [Supervisor](#supervisor-configuration) so that your workers are automatically restarted after processing a given number of jobs:
+The `--max-jobs` option may be used to instruct the worker to process the given number of jobs and then exit. This option may be useful when combined with [Supervisor](#supervisor-configuration) so that your workers are automatically restarted after processing a given number of jobs, releasing any memory they may have accumulated:
php artisan queue:work --max-jobs=1000
#### Processing All Queued Jobs & Then Exiting
-The `--stop-when-empty` option may be used to instruct the worker to process all jobs and then exit gracefully. This option can be useful when working Laravel queues within a Docker container if you wish to shutdown the container after the queue is empty:
+The `--stop-when-empty` option may be used to instruct the worker to process all jobs and then exit gracefully. This option can be useful when processing Laravel queues within a Docker container if you wish to shutdown the container after the queue is empty:
php artisan queue:work --stop-when-empty
#### Processing Jobs For A Given Number Of Seconds
-The `--max-time` option may be used to instruct the worker to process jobs for the given number of seconds and then exit. This option may be useful when combined with [Supervisor](#supervisor-configuration) so that your workers are automatically restarted after processing jobs for a given amount of time:
+The `--max-time` option may be used to instruct the worker to process jobs for the given number of seconds and then exit. This option may be useful when combined with [Supervisor](#supervisor-configuration) so that your workers are automatically restarted after processing jobs for a given amount of time, releasing any memory they may have accumulated:
// Process jobs for one hour and then exit...
php artisan queue:work --max-time=3600
+
+#### Worker Sleep Duration
+
+When jobs are available on the queue, the worker will keep processing jobs with no delay in between them. However, the `sleep` option determines how any seconds the worker will "sleep" if there are no new jobs available. While sleeping, the worker will not process any new jobs - the jobs will be processed after the worker wakes up again.
+
+ php artisan queue:work --sleep=3
+
#### Resource Considerations
-Daemon queue workers do not "reboot" the framework before processing each job. Therefore, you should free any heavy resources after each job completes. For example, if you are doing image manipulation with the GD library, you should free the memory with `imagedestroy` when you are done.
+Daemon queue workers do not "reboot" the framework before processing each job. Therefore, you should release any heavy resources after each job completes. For example, if you are doing image manipulation with the GD library, you should free the memory with `imagedestroy` when you are done processing the image.
### Queue Priorities
-Sometimes you may wish to prioritize how your queues are processed. For example, in your `config/queue.php` you may set the default `queue` for your `redis` connection to `low`. However, occasionally you may wish to push a job to a `high` priority queue like so:
+Sometimes you may wish to prioritize how your queues are processed. For example, in your `config/queue.php` configuration file you may set the default `queue` for your `redis` connection to `low`. However, occasionally you may wish to push a job to a `high` priority queue like so:
dispatch((new Job)->onQueue('high'));
@@ -1125,13 +1338,13 @@ To start a worker that verifies that all of the `high` queue jobs are processed
### Queue Workers & Deployment
-Since queue workers are long-lived processes, they will not pick up changes to your code without being restarted. So, the simplest way to deploy an application using queue workers is to restart the workers during your deployment process. You may gracefully restart all of the workers by issuing the `queue:restart` command:
+Since queue workers are long-lived processes, they will not notice changes to your code without being restarted. So, the simplest way to deploy an application using queue workers is to restart the workers during your deployment process. You may gracefully restart all of the workers by issuing the `queue:restart` command:
php artisan queue:restart
-This command will instruct all queue workers to gracefully "die" after they finish processing their current job so that no existing jobs are lost. Since the queue workers will die when the `queue:restart` command is executed, you should be running a process manager such as [Supervisor](#supervisor-configuration) to automatically restart the queue workers.
+This command will instruct all queue workers to gracefully exit after they finish processing their current job so that no existing jobs are lost. Since the queue workers will exit when the `queue:restart` command is executed, you should be running a process manager such as [Supervisor](#supervisor-configuration) to automatically restart the queue workers.
-> {tip} The queue uses the [cache](/docs/{{version}}/cache) to store restart signals, so you should verify a cache driver is properly configured for your application before using this feature.
+> {tip} The queue uses the [cache](/docs/{{version}}/cache) to store restart signals, so you should verify that a cache driver is properly configured for your application before using this feature.
### Job Expirations & Timeouts
@@ -1139,57 +1352,60 @@ This command will instruct all queue workers to gracefully "die" after they fini
#### Job Expiration
-In your `config/queue.php` configuration file, each queue connection defines a `retry_after` option. This option specifies how many seconds the queue connection should wait before retrying a job that is being processed. For example, if the value of `retry_after` is set to `90`, the job will be released back onto the queue if it has been processing for 90 seconds without being deleted. Typically, you should set the `retry_after` value to the maximum number of seconds your jobs should reasonably take to complete processing.
+In your `config/queue.php` configuration file, each queue connection defines a `retry_after` option. This option specifies how many seconds the queue connection should wait before retrying a job that is being processed. For example, if the value of `retry_after` is set to `90`, the job will be released back onto the queue if it has been processing for 90 seconds without being released or deleted. Typically, you should set the `retry_after` value to the maximum number of seconds your jobs should reasonably take to complete processing.
> {note} The only queue connection which does not contain a `retry_after` value is Amazon SQS. SQS will retry the job based on the [Default Visibility Timeout](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/AboutVT.html) which is managed within the AWS console.
#### Worker Timeouts
-The `queue:work` Artisan command exposes a `--timeout` option. The `--timeout` option specifies how long the Laravel queue master process will wait before killing off a child queue worker that is processing a job. Sometimes a child queue process can become "frozen" for various reasons. The `--timeout` option removes frozen processes that have exceeded that specified time limit:
+The `queue:work` Artisan command exposes a `--timeout` option. If a job is processing for longer than the number of seconds specified by the timeout value, the worker processing the job will exit with an error. Typically, the worker will be restarted automatically by a [process manager configured on your server](#supervisor-configuration):
- php artisan queue:work --timeout=60
+```bash
+php artisan queue:work --timeout=60
+```
The `retry_after` configuration option and the `--timeout` CLI option are different, but work together to ensure that jobs are not lost and that jobs are only successfully processed once.
-> {note} The `--timeout` value should always be at least several seconds shorter than your `retry_after` configuration value. This will ensure that a worker processing a given job is always killed before the job is retried. If your `--timeout` option is longer than your `retry_after` configuration value, your jobs may be processed twice.
-
-
-#### Worker Sleep Duration
-
-When jobs are available on the queue, the worker will keep processing jobs with no delay in between them. However, the `sleep` option determines how long (in seconds) the worker will "sleep" if there are no new jobs available. While sleeping, the worker will not process any new jobs - the jobs will be processed after the worker wakes up again.
-
- php artisan queue:work --sleep=3
+> {note} The `--timeout` value should always be at least several seconds shorter than your `retry_after` configuration value. This will ensure that a worker processing a frozen job is always terminated before the job is retried. If your `--timeout` option is longer than your `retry_after` configuration value, your jobs may be processed twice.
## Supervisor Configuration
+In production, you need a way to keep your `queue:work` processes running. A `queue:work` process may stop running for a variety of reasons, such as an exceeded worker timeout or the execution of the `queue:restart` command.
+
+For this reason, you need to configure a process monitor that can detect when your `queue:work` processes exit and automatically restart them. In addition, process monitors can allow you to specify how many `queue:work` processes you would like to run concurrently. Supervisor is a process monitor commonly used in Linux environments and we will discuss how to configure it in the following documentation.
+
#### Installing Supervisor
-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:
+Supervisor is a process monitor for the Linux operating system, and will automatically restart your `queue:work` processes if they fail. To install Supervisor on Ubuntu, you may use the following command:
sudo apt-get install supervisor
-> {tip} If configuring Supervisor yourself sounds overwhelming, consider using [Laravel Forge](https://forge.laravel.com), which will automatically install and configure Supervisor for your Laravel projects.
+> {tip} If configuring and managing Supervisor yourself sounds overwhelming, consider using [Laravel Forge](https://forge.laravel.com), which will automatically install and configure Supervisor for your production Laravel projects.
#### Configuring Supervisor
-Supervisor configuration files are typically stored in the `/etc/supervisor/conf.d` directory. Within this directory, you may create any number of configuration files that instruct supervisor how your processes should be monitored. For example, let's create a `laravel-worker.conf` file that starts and monitors a `queue:work` process:
-
- [program:laravel-worker]
- process_name=%(program_name)s_%(process_num)02d
- command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --max-time=3600
- autostart=true
- autorestart=true
- user=forge
- numprocs=8
- redirect_stderr=true
- stdout_logfile=/home/forge/app.com/worker.log
- stopwaitsecs=3600
-
-In this example, the `numprocs` directive will instruct Supervisor to run 8 `queue:work` processes and monitor all of them, automatically restarting them if they fail. You should change the `queue:work sqs` portion of the `command` directive to reflect your desired queue connection.
+Supervisor configuration files are typically stored in the `/etc/supervisor/conf.d` directory. Within this directory, you may create any number of configuration files that instruct supervisor how your processes should be monitored. For example, let's create a `laravel-worker.conf` file that starts and monitors `queue:work` processes:
+
+```ini
+[program:laravel-worker]
+process_name=%(program_name)s_%(process_num)02d
+command=php /home/forge/app.com/artisan queue:work sqs --sleep=3 --tries=3 --max-time=3600
+autostart=true
+autorestart=true
+stopasgroup=true
+killasgroup=true
+user=forge
+numprocs=8
+redirect_stderr=true
+stdout_logfile=/home/forge/app.com/worker.log
+stopwaitsecs=3600
+```
+
+In this example, the `numprocs` directive will instruct Supervisor to run eight `queue:work` processes and monitor all of them, automatically restarting them if they fail. You should change the `command` directive of the configuration to reflect your desired queue connection and worker options.
> {note} You should ensure that the value of `stopwaitsecs` is greater than the number of seconds consumed by your longest running job. Otherwise, Supervisor may kill the job before it is finished processing.
@@ -1198,32 +1414,34 @@ 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:
- sudo supervisorctl reread
+```bash
+sudo supervisorctl reread
- sudo supervisorctl update
+sudo supervisorctl update
- sudo supervisorctl start laravel-worker:*
+sudo supervisorctl start laravel-worker:*
+```
For more information on Supervisor, consult the [Supervisor documentation](http://supervisord.org/index.html).
## Dealing With Failed Jobs
-Sometimes your queued jobs will fail. Don't worry, things don't always go as planned! Laravel includes a convenient way to specify the maximum number of times a job should be attempted. After a job has exceeded this amount of attempts, it will be inserted into the `failed_jobs` database table. To create a migration for the `failed_jobs` table, you may use the `queue:failed-table` command:
+Sometimes your queued jobs will fail. Don't worry, things don't always go as planned! Laravel includes a convenient way to [specify the maximum number of times a job should be attempted](#max-job-attempts-and-timeout). After a job has exceeded this number of attempts, it will be inserted into the `failed_jobs` database table. Of course, we will need to create that table if it does not already exist. To create a migration for the `failed_jobs` table, you may use the `queue:failed-table` command:
php artisan queue:failed-table
php artisan migrate
-Then, when running your [queue worker](#running-the-queue-worker), you can specify the maximum number of times a job should be attempted using the `--tries` switch on the `queue:work` command. If you do not specify a value for the `--tries` option, jobs will only be attempted once:
+When running a [queue worker](#running-the-queue-worker) process, you may specify the maximum number of times a job should be attempted using the `--tries` switch on the `queue:work` command. If you do not specify a value for the `--tries` option, jobs will only be attempted once or as many times as specified by the job class' `$tries` property:
php artisan queue:work redis --tries=3
-In addition, you may specify how many seconds Laravel should wait before retrying a job that has failed using the `--backoff` option. By default, a job is retried immediately:
+Using the `--backoff` option, you may specify how many seconds Laravel should wait before retrying a job that has encountered an exception. By default, a job is immediately released back onto the queue so that it may be attempted again:
php artisan queue:work redis --tries=3 --backoff=3
-If you would like to configure the failed job retry delay on a per-job basis, you may do so by defining a `backoff` property on your queued job class:
+If you would like to configure how many seconds Laravel should wait before retrying a job that has encountered an exception on a per-job basis, you may do so by defining a `backoff` property on your job class:
/**
* The number of seconds to wait before retrying the job.
@@ -1232,7 +1450,7 @@ If you would like to configure the failed job retry delay on a per-job basis, yo
*/
public $backoff = 3;
-If you require more complex logic for determining the retry delay, you may define a `backoff` method on your queued job class:
+If you require more complex logic for determining the job's backoff time, you may define a `backoff` method on your job class:
/**
* Calculate the number of seconds to wait before retrying the job.
@@ -1244,7 +1462,7 @@ If you require more complex logic for determining the retry delay, you may defin
return 3;
}
-You may easily configure "exponential" backoffs by returning an array of backoff values from the `backoff` method. In this example, the retry delay will be 1 seconds for the first retry, 5 seconds for the second retry, and 10 seconds for the third retry:
+You may easily configure "exponential" backoffs by returning an array of backoff values from the `backoff` method. In this example, the retry delay will be 1 second for the first retry, 5 seconds for the second retry, and 10 seconds for the third retry:
/**
* Calculate the number of seconds to wait before retrying the job.
@@ -1259,7 +1477,7 @@ You may easily configure "exponential" backoffs by returning an array of backoff
### Cleaning Up After Failed Jobs
-You may define a `failed` method directly on your job class, allowing you to perform job specific clean-up when a failure occurs. This is the perfect location to send an alert to your users or revert any actions performed by the job. The `Throwable` exception that caused the job to fail will be passed to the `failed` method:
+When a particular job fails, you may want to send an alert to your users or revert any actions that were partially completed by the job. To accomplish this, you may define a `failed` method on your job class. The `Throwable` instance that caused the job to fail will be passed to the `failed` method:
-### Failed Job Events
-
-If you would like to register an event that will be called when a job fails, you may use the `Queue::failing` method. This event is a great opportunity to notify your team via email or [Slack](https://www.slack.com). For example, we may attach a callback to this event from the `AppServiceProvider` that is included with Laravel:
-
- connectionName
- // $event->job
- // $event->exception
- });
- }
- }
-
### Retrying Failed Jobs
-To view all of your failed jobs that have been inserted into your `failed_jobs` database table, you may use the `queue:failed` Artisan command:
+To view all of the failed jobs that have been inserted into your `failed_jobs` database table, you may use the `queue:failed` Artisan command:
php artisan queue:failed
@@ -1380,16 +1563,16 @@ If you would like to delete a failed job, you may use the `queue:forget` command
> {tip} When using [Horizon](/docs/{{version}}/horizon), you should use the `horizon:forget` command to delete a failed job instead of the `queue:forget` command.
-To delete all of your failed jobs, you may use the `queue:flush` command:
+To delete all of your failed jobs from the `failed_jobs` table, you may use the `queue:flush` command:
php artisan queue:flush
### Ignoring Missing Models
-When injecting an Eloquent model into a job, it is automatically serialized before being placed on the queue and restored when the job is processed. However, if the model has been deleted while the job was waiting to be processed by a worker, your job may fail with a `ModelNotFoundException`.
+When injecting an Eloquent model into a job, the model is automatically serialized before being placed on the queue and re-retrieved from the database when the job is processed. However, if the model has been deleted while the job was waiting to be processed by a worker, your job may fail with a `ModelNotFoundException`.
-For convenience, you may choose to automatically delete jobs with missing models by setting your job's `deleteWhenMissingModels` property to `true`:
+For convenience, you may choose to automatically delete jobs with missing models by setting your job's `deleteWhenMissingModels` property to `true`. When this model is set to `true`, Laravel will quietly discard the job without raising an exception:
/**
* Delete the job if its models no longer exist.
@@ -1398,6 +1581,46 @@ For convenience, you may choose to automatically delete jobs with missing models
*/
public $deleteWhenMissingModels = true;
+
+### Failed Job Events
+
+If you would like to register an event listener that will be invoked when a job fails, you may use the `Queue` facade's `failing` method. For example, we may attach a closure to this event from the `boot` method of the `AppServiceProvider` that is included with Laravel:
+
+ connectionName
+ // $event->job
+ // $event->exception
+ });
+ }
+ }
+
## Clearing Jobs From Queues
@@ -1416,7 +1639,7 @@ You may also provide the `connection` argument and `queue` option to delete jobs
## Job Events
-Using the `before` and `after` methods on the `Queue` [facade](/docs/{{version}}/facades), you may specify callbacks to be executed before or after a queued job is processed. These callbacks are a great opportunity to perform additional logging or increment statistics for a dashboard. Typically, you should call these methods from a [service provider](/docs/{{version}}/providers). For example, we may use the `AppServiceProvider` that is included with Laravel:
+Using the `before` and `after` methods on the `Queue` [facade](/docs/{{version}}/facades), you may specify callbacks to be executed before or after a queued job is processed. These callbacks are a great opportunity to perform additional logging or increment statistics for a dashboard. Typically, you should call these methods from the `boot` method of a [service provider](/docs/{{version}}/providers). For example, we may use the `AppServiceProvider` that is included with Laravel:
0) {
diff --git a/redirects.md b/redirects.md
index 4a7d022887a..73fe4bc0516 100644
--- a/redirects.md
+++ b/redirects.md
@@ -82,6 +82,10 @@ Redirecting to a new URL and [flashing data to the session](/docs/{{version}}/se
return redirect('dashboard')->with('status', 'Profile updated!');
});
+You may use the `withInput` method provided by the `RedirectResponse` instance to flash the current request's input data to the session before redirecting the user to a new location. Once the input has been flashed to the session, you may easily [retrieve it](/docs/{{version}}/requests#retrieving-old-input) during the next request:
+
+ return back()->withInput();
+
After the user is redirected, you may display the flashed message from the [session](/docs/{{version}}/session). For example, using [Blade syntax](/docs/{{version}}/blade):
@if (session('status'))
diff --git a/redis.md b/redis.md
index 8f8dcbdd23f..67ff922997f 100644
--- a/redis.md
+++ b/redis.md
@@ -1,10 +1,12 @@
# Redis
- [Introduction](#introduction)
- - [Configuration](#configuration)
+- [Configuration](#configuration)
+ - [Clusters](#clusters)
- [Predis](#predis)
- - [PhpRedis](#phpredis)
+ - [phpredis](#phpredis)
- [Interacting With Redis](#interacting-with-redis)
+ - [Transactions](#transactions)
- [Pipelining Commands](#pipelining-commands)
- [Pub / Sub](#pubsub)
@@ -13,16 +15,18 @@
[Redis](https://redis.io) is an open source, advanced key-value store. It is often referred to as a data structure server since keys can contain [strings](https://redis.io/topics/data-types#strings), [hashes](https://redis.io/topics/data-types#hashes), [lists](https://redis.io/topics/data-types#lists), [sets](https://redis.io/topics/data-types#sets), and [sorted sets](https://redis.io/topics/data-types#sorted-sets).
-Before using Redis with Laravel, we encourage you to install and use the [PhpRedis](https://github.com/phpredis/phpredis) PHP extension via PECL. The extension is more complex to install but may yield better performance for applications that make heavy use of Redis.
+Before using Redis with Laravel, we encourage you to install and use the [phpredis](https://github.com/phpredis/phpredis) PHP extension via PECL. The extension is more complex to install compared to "user-land" PHP packages but may yield better performance for applications that make heavy use of Redis. If you are using [Laravel Sail](/docs/{{version}}/sail), this extension is already installed in your application's Docker container.
-Alternatively, you can install the `predis/predis` package via Composer:
+If you are unable to install the phpredis extension, you may install the `predis/predis` package via Composer. Predis is a Redis client written entirely in PHP and does not require any additional extensions:
- composer require predis/predis
+```bash
+composer require predis/predis
+```
-### Configuration
+## Configuration
-The Redis configuration for your application is located in the `config/database.php` configuration file. Within this file, you will see a `redis` array containing the Redis servers utilized by your application:
+You may configure your application's Redis settings via the `config/database.php` configuration file. Within this file, you will see a `redis` array containing the Redis servers utilized by your application:
'redis' => [
@@ -44,7 +48,7 @@ The Redis configuration for your application is located in the `config/database.
],
-The default server configuration should suffice for development. However, you are free to modify this array based on your environment. Each Redis server defined in your configuration file is required to have a name, host, and port unless you define a single URL to represent the Redis connection:
+Each Redis server defined in your configuration file is required to have a name, host, and a port unless you define a single URL to represent the Redis connection:
'redis' => [
@@ -63,7 +67,7 @@ The default server configuration should suffice for development. However, you ar
#### Configuring The Connection Scheme
-By default, Redis clients will use the `tcp` scheme when connecting to your Redis servers; however, you may use TLS / SSL encryption by specifying a `scheme` configuration option in your Redis server configuration:
+By default, Redis clients will use the `tcp` scheme when connecting to your Redis servers; however, you may use TLS / SSL encryption by specifying a `scheme` configuration option in your Redis server's configuration array:
'redis' => [
@@ -79,10 +83,10 @@ By default, Redis clients will use the `tcp` scheme when connecting to your Redi
],
-
-#### Configuring Clusters
+
+### Clusters
-If your application is utilizing a cluster of Redis servers, you should define these clusters within a `clusters` key of your Redis configuration:
+If your application is utilizing a cluster of Redis servers, you should define these clusters within a `clusters` key of your Redis configuration. This configuration key does not exist by default so you will need to create it within your application's `config/database.php` configuration file:
'redis' => [
@@ -101,7 +105,9 @@ If your application is utilizing a cluster of Redis servers, you should define t
],
-By default, clusters will perform client-side sharding across your nodes, allowing you to pool nodes and create a large amount of available RAM. However, note that client-side sharding does not handle failover; therefore, it is primarily suited for cached data that is available from another primary data store. If you would like to use native Redis clustering, you should specify this in the `options` key of your Redis configuration:
+By default, clusters will perform client-side sharding across your nodes, allowing you to pool nodes and create a large amount of available RAM. However, client-side sharding does not handle failover; therefore, it is primarily suited for transient cached data that is available from another primary data store.
+
+If you would like to use native Redis clustering instead of client-side sharding, you may specify this by setting the `options.cluster` configuration value to `redis` within your application's `config/database.php` configuration file:
'redis' => [
@@ -120,7 +126,7 @@ By default, clusters will perform client-side sharding across your nodes, allowi
### Predis
-To utilize the Predis extension, you should change the `REDIS_CLIENT` environment variable from `phpredis` to `predis`:
+If you would like your application to interact with Redis via the Predis package, you should ensure the `REDIS_CLIENT` environment variable's value is `predis`:
'redis' => [
@@ -129,7 +135,7 @@ To utilize the Predis extension, you should change the `REDIS_CLIENT` environmen
// Rest of Redis configuration...
],
-In addition to the default `host`, `port`, `database`, and `password` server configuration options, Predis supports additional [connection parameters](https://github.com/nrk/predis/wiki/Connection-Parameters) that may be defined for each of your Redis servers. To utilize these additional configuration options, add them to your Redis server configuration in the `config/database.php` configuration file:
+In addition to the default `host`, `port`, `database`, and `password` server configuration options, Predis supports additional [connection parameters](https://github.com/nrk/predis/wiki/Connection-Parameters) that may be defined for each of your Redis servers. To utilize these additional configuration options, add them to your Redis server configuration in your application's `config/database.php` configuration file:
'default' => [
'host' => env('REDIS_HOST', 'localhost'),
@@ -139,10 +145,15 @@ In addition to the default `host`, `port`, `database`, and `password` server con
'read_write_timeout' => 60,
],
+
+#### The Redis Facade Alias
+
+Laravel's `config/app.php` configuration file contains an `aliases` array which defines all of the class aliases that will be registered by the framework. For convenience, an alias entry is included for each [facade](/docs/{{version}}/facades) offered by Laravel; however, the `Redis` alias is disabled because it conflicts with the `Redis` class name provided by the phpredis extension. If you are using the Predis client and would like to enable this alias, you may un-comment the alias in your application's `config/app.php` configuration file.
+
-### PhpRedis
+### phpredis
-The PhpRedis extension is configured as default at `REDIS_CLIENT` env and in your `config/database.php`:
+By default, Laravel will use the phpredis extension to communicate with Redis. The client that Laravel will use to communicate with Redis is dictated by the value of the `redis.client` configuration option, which typically reflects the value of the `REDIS_CLIENT` environment variable:
'redis' => [
@@ -151,11 +162,7 @@ The PhpRedis extension is configured as default at `REDIS_CLIENT` env and in you
// Rest of Redis configuration...
],
-If you plan to use PhpRedis extension along with the `Redis` Facade alias, you should rename it to something else, like `RedisManager`, to avoid a collision with the Redis class. You can do that in the aliases section of your `app.php` config file.
-
- 'RedisManager' => Illuminate\Support\Facades\Redis::class,
-
-In addition to the default `host`, `port`, `database`, and `password` server configuration options, PhpRedis supports the following additional connection parameters: `persistent`, `prefix`, `read_timeout`, `retry_interval`, `timeout`, and `context`. You may add any of these options to your Redis server configuration in the `config/database.php` configuration file:
+In addition to the default `host`, `port`, `database`, and `password` server configuration options, phpredis supports the following additional connection parameters: `persistent`, `prefix`, `read_timeout`, `retry_interval`, `timeout`, and `context`. You may add any of these options to your Redis server configuration in the `config/database.php` configuration file:
'default' => [
'host' => env('REDIS_HOST', 'localhost'),
@@ -169,11 +176,6 @@ In addition to the default `host`, `port`, `database`, and `password` server con
],
],
-
-#### The Redis Facade
-
-To avoid class naming collisions with the Redis PHP extension itself, you will need to delete or rename the `Illuminate\Support\Facades\Redis` facade alias from your `app` configuration file's `aliases` array. Generally, you should remove this alias entirely and only reference the facade by its fully qualified class name while using the Redis PHP extension.
-
## Interacting With Redis
@@ -192,41 +194,79 @@ You may interact with Redis by calling various methods on the `Redis` [facade](/
* Show the profile for the given user.
*
* @param int $id
- * @return Response
+ * @return \Illuminate\Http\Response
*/
- public function showProfile($id)
+ public function show($id)
{
- $user = Redis::get('user:profile:'.$id);
-
- return view('user.profile', ['user' => $user]);
+ return view('user.profile', [
+ 'user' => Redis::get('user:profile:'.$id)
+ ]);
}
}
-As mentioned above, you may call any of the Redis commands on the `Redis` facade. Laravel uses magic methods to pass the commands to the Redis server, so pass the arguments the Redis command expects:
+As mentioned above, you may call any of Redis' commands on the `Redis` facade. Laravel uses magic methods to pass the commands to the Redis server. If a Redis command expects arguments, you should pass those to the facade's corresponding method:
+
+ use Illuminate\Support\Facades\Redis;
Redis::set('name', 'Taylor');
$values = Redis::lrange('names', 5, 10);
-Alternatively, you may also pass commands to the server using the `command` method, which accepts the name of the command as its first argument, and an array of values as its second argument:
+Alternatively, you may pass commands to the server using the `Redis` facade's `command` method, which accepts the name of the command as its first argument and an array of values as its second argument:
$values = Redis::command('lrange', ['name', 5, 10]);
#### Using Multiple Redis Connections
-You may get a Redis instance by calling the `Redis::connection` method:
+Your application's `config/database.php` configuration file allows you to define multiple Redis connections / servers. You may obtain a connection to a specific Redis connection using the `Redis` facade's `connection` method:
+
+ $redis = Redis::connection('connection-name');
+
+To obtain an instance of the default Redis connection, you may call the `connection` method without any additional arguments:
$redis = Redis::connection();
-This will give you an instance of the default Redis server. You may also pass the connection or cluster name to the `connection` method to get a specific server or cluster as defined in your Redis configuration:
+
+### Transactions
- $redis = Redis::connection('my-connection');
+The `Redis` facade's `transaction` method provides a convenient wrapper around Redis' native `MULTI` and `EXEC` commands. The `transaction` method accepts a closure as its only argument. This closure will receive a Redis connection instance and may issue any commands it would like to this instance. All of the Redis commands issued within the closure will be executed in a single, atomic transaction:
+
+ use Illuminate\Support\Facades\Redis;
+
+ Redis::transaction(function ($redis) {
+ $redis->incr('user_visits', 1);
+ $redis->incr('total_visits', 1);
+ });
+
+> {note} When defining a Redis transaction, you may not retrieve any values from the Redis connection. Remember, your transaction is executed as a single, atomic operation and that operation is not executed until your entire closure has finished executing its commands.
+
+#### Lua Scripts
+
+The `eval` method provides another method of executing multiple Redis commands in a single, atomic operation. However, the `eval` method has the benefit of being able to interact with and inspect Redis key values during that operation. Redis scripts are written in the [Lua programming language](https://www.lua.org).
+
+The `eval` method can be a bit scary at first, but we'll explore a basic example to break the ice. The `eval` method expects several arguments. First, you should pass the Lua script (as a string) to the method. Secondly, you should pass the number of keys (as an integer) that the script interacts with. Thirdly, you should pass the names of those keys. Finally, you may pass any other additional arguments that you need to access within your script.
+
+In this example, we will increment a counter, inspect its new value, and increment a second counter if the first counter's value is greater than five. Finally, we will return the value of the first counter:
+
+ $value = Redis::eval(<<<'LUA'
+ local counter = redis.call("incr", KEYS[1])
+
+ if counter > 5 then
+ redis.call("incr", KEYS[2])
+ end
+
+ return counter
+ LUA, 2, 'first-counter', 'second-counter');
+
+> {note} Please consult the [Redis documentation](https://redis.io/commands/eval) for more information on Redis scripting.
### Pipelining Commands
-Pipelining should be used when you need to send many commands to the server. The `pipeline` method accepts one argument: a `Closure` that receives a Redis instance. You may issue all of your commands to this Redis instance and they will all be streamed to the server thus providing better performance:
+Sometimes you may need to execute dozens of Redis commands. Instead of making a network trip to your Redis server for each command, you may use the `pipeline` method. The `pipeline` method accepts one argument: a closure that receives a Redis instance. You may issue all of your commands to this Redis instance and they will all be sent to the Redis server at the same time to reduce network trips to the server. The commands will still be executed in the order they were issued:
+
+ use Illuminate\Support\Facades\Redis;
Redis::pipeline(function ($pipe) {
for ($i = 0; $i < 1000; $i++) {
@@ -279,16 +319,20 @@ First, let's setup a channel listener using the `subscribe` method. We'll place
Now we may publish messages to the channel using the `publish` method:
- Route::get('publish', function () {
- // Route logic...
+ use Illuminate\Support\Facades\Redis;
+
+ Route::get('/publish', function () {
+ // ...
- Redis::publish('test-channel', json_encode(['foo' => 'bar']));
+ Redis::publish('test-channel', json_encode([
+ 'name' => 'Adam Wathan'
+ ]));
});
#### Wildcard Subscriptions
-Using the `psubscribe` method, you may subscribe to a wildcard channel, which may be useful for catching all messages on all channels. The `$channel` name will be passed as the second argument to the provided callback `Closure`:
+Using the `psubscribe` method, you may subscribe to a wildcard channel, which may be useful for catching all messages on all channels. The channel name will be passed as the second argument to the provided closure:
Redis::psubscribe(['*'], function ($message, $channel) {
echo $message;
diff --git a/releases.md b/releases.md
index 9e35f78f402..e2027c393ae 100644
--- a/releases.md
+++ b/releases.md
@@ -176,7 +176,7 @@ _Rate limiting improvements were contributed by [Taylor Otwell](https://github.c
Laravel's request rate limiter feature has been augmented with more flexibility and power, while still maintaining backwards compatibility with previous release's `throttle` middleware API.
-Rate limiters are defined using the `RateLimiter` facade's `for` method. The `for` method accepts a rate limiter name and a Closure that returns the limit configuration that should apply to routes that are assigned this rate limiter:
+Rate limiters are defined using the `RateLimiter` facade's `for` method. The `for` method accepts a rate limiter name and a closure that returns the limit configuration that should apply to routes that are assigned this rate limiter:
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
@@ -246,7 +246,7 @@ For this reason, Laravel now allows you to pre-render a maintenance mode view th
_Catch improvements were contributed by [Mohamed Said](https://github.com/themsaid)_.
-Using the new `catch` method, you may now provide a Closure that should be executed if a queued Closure fails to complete successfully after exhausting all of your queue's configured retry attempts:
+Using the new `catch` method, you may now provide a closure that should be executed if a queued closure fails to complete successfully after exhausting all of your queue's configured retry attempts:
use Throwable;
@@ -272,7 +272,7 @@ To learn more about Blade components, please consult the [Blade documentation](/
_Event listener improvements were contributed by [Taylor Otwell](https://github.com/taylorotwell)_.
-Closure based event listeners may now be registered by only passing the Closure to the `Event::listen` method. Laravel will inspect the Closure to determine which type of event the listener handles:
+Closure based event listeners may now be registered by only passing the closure to the `Event::listen` method. Laravel will inspect the closure to determine which type of event the listener handles:
use App\Events\PodcastProcessed;
use Illuminate\Support\Facades\Event;
@@ -281,7 +281,7 @@ Closure based event listeners may now be registered by only passing the Closure
//
});
-In addition, Closure based event listeners may now be marked as queueable using the `Illuminate\Events\queueable` function:
+In addition, closure based event listeners may now be marked as queueable using the `Illuminate\Events\queueable` function:
use App\Events\PodcastProcessed;
use function Illuminate\Events\queueable;
@@ -297,7 +297,7 @@ Like queued jobs, you may use the `onConnection`, `onQueue`, and `delay` methods
//
})->onConnection('redis')->onQueue('podcasts')->delay(now()->addSeconds(10)));
-If you would like to handle anonymous queued listener failures, you may provide a Closure to the `catch` method while defining the `queueable` listener:
+If you would like to handle anonymous queued listener failures, you may provide a closure to the `catch` method while defining the `queueable` listener:
use App\Events\PodcastProcessed;
use function Illuminate\Events\queueable;
diff --git a/requests.md b/requests.md
index f0642f1b53d..7fd5da8345b 100644
--- a/requests.md
+++ b/requests.md
@@ -1,21 +1,36 @@
# HTTP Requests
-- [Accessing The Request](#accessing-the-request)
+- [Introduction](#introduction)
+- [Interacting With The Request](#interacting-with-the-request)
+ - [Accessing The Request](#accessing-the-request)
- [Request Path & Method](#request-path-and-method)
+ - [Request Headers](#request-headers)
+ - [Request IP Address](#request-ip)
+ - [Content Negotiation](#content-negotiation)
- [PSR-7 Requests](#psr7-requests)
-- [Input Trimming & Normalization](#input-trimming-and-normalization)
-- [Retrieving Input](#retrieving-input)
+- [Input](#input)
+ - [Retrieving Input](#retrieving-input)
+ - [Determining If Input Is Present](#determining-if-input-is-present)
- [Old Input](#old-input)
- [Cookies](#cookies)
+ - [Input Trimming & Normalization](#input-trimming-and-normalization)
- [Files](#files)
- [Retrieving Uploaded Files](#retrieving-uploaded-files)
- [Storing Uploaded Files](#storing-uploaded-files)
- [Configuring Trusted Proxies](#configuring-trusted-proxies)
+
+## Introduction
+
+Laravel's `Illuminate\Http\Request` class provides an object-oriented way to interact with the current HTTP request being handled by your application as well retrieve the input, cookies, and files that were submitted with the request.
+
+
+## Interacting With The Request
+
-## Accessing The Request
+### Accessing The Request
-To obtain an instance of the current HTTP request via dependency injection, you should type-hint the `Illuminate\Http\Request` class on your controller method. The incoming request instance will automatically be injected by the [service container](/docs/{{version}}/container):
+To obtain an instance of the current HTTP request via dependency injection, you should type-hint the `Illuminate\Http\Request` class on your route closure or controller method. The incoming request instance will automatically be injected by the Laravel [service container](/docs/{{version}}/container):
#### Dependency Injection & Route Parameters
@@ -48,7 +71,7 @@ If your controller method is also expecting input from a route parameter you sho
Route::put('user/{id}', [UserController::class, 'update']);
-You may still type-hint the `Illuminate\Http\Request` and access your route parameter `id` by defining your controller method as follows:
+You may still type-hint the `Illuminate\Http\Request` and access your `id` route parameter by defining your controller method as follows:
-#### Accessing The Request Via Route Closures
-
-You may also type-hint the `Illuminate\Http\Request` class on a route Closure. The service container will automatically inject the incoming request into the Closure when it is executed:
-
- use Illuminate\Http\Request;
-
- Route::get('/', function (Request $request) {
- //
- });
-
### Request Path & Method
-The `Illuminate\Http\Request` instance provides a variety of methods for examining the HTTP request for your application and extends the `Symfony\Component\HttpFoundation\Request` class. We will discuss a few of the most important methods below.
+The `Illuminate\Http\Request` instance provides a variety of methods for examining the incoming HTTP request and extends the `Symfony\Component\HttpFoundation\Request` class. We will discuss a few of the most important methods below.
#### Retrieving The Request Path
-The `path` method returns the request's path information. So, if the incoming request is targeted at `http://domain.com/foo/bar`, the `path` method will return `foo/bar`:
+The `path` method returns the request's path information. So, if the incoming request is targeted at `http://example.com/foo/bar`, the `path` method will return `foo/bar`:
$uri = $request->path();
+
+#### Inspecting The Request Path / Route
+
The `is` method allows you to verify that the incoming request path matches a given pattern. You may use the `*` character as a wildcard when utilizing this method:
if ($request->is('admin/*')) {
//
}
+Using the `routeIs` method, you may determine if the incoming request has matched a [named route](/docs/{{version}}/routing#named-routes):
+
+ if ($request->routeIs('admin.*')) {
+ //
+ }
+
#### Retrieving The Request URL
To retrieve the full URL for the incoming request you may use the `url` or `fullUrl` methods. The `url` method will return the URL without the query string, while the `fullUrl` method includes the query string:
- // Without Query String...
$url = $request->url();
- // With Query String...
- $url = $request->fullUrl();
+ $urlWithQueryString = $request->fullUrl();
#### Retrieving The Request Method
@@ -122,6 +141,55 @@ The `method` method will return the HTTP verb for the request. You may use the `
//
}
+
+### Request Headers
+
+You may retrieve a request header from the `Illuminate\Http\Request` instance using the `header` method. If the header is not present on the request, `null` will be returned. However, the `header` method accepts an optional second argument that will be returned if the header is not present on the request:
+
+ $value = $request->header('X-Header-Name');
+
+ $value = $request->header('X-Header-Name', 'default');
+
+The `hasHeader` method may be used to determine if the request contains a given header:
+
+ if ($request->hasHeader('X-Header-Name')) {
+ //
+ }
+
+For convenience, the `bearerToken` may be used to a bearer token from the `Authorization` header. If no such header is present, an empty string will be returned:
+
+ $token = $request->bearerToken();
+
+
+### Request IP Address
+
+The `ip` method may be used to retrieve the IP address of the client that made the request to your application:
+
+ $ipAddress = $request->ip();
+
+
+### Content Negotiation
+
+Laravel provides several methods for inspecting the incoming request's requested content types via the `Accept` header. First, the `getAcceptableContentTypes` method will return an array containing all of the content types accepted by the request:
+
+ $contentTypes = $request->getAcceptableContentTypes();
+
+The `accepts` method accepts an array of content types and returns `true` if any of the content types are accepted by the request. Otherwise, `false` will be returned:
+
+ if ($request->accepts(['text/html', 'application/json'])) {
+ // ...
+ }
+
+You may use the `prefers` method to determine which content type out of a given array of content types is most preferred by the request. If none of the provided content types are accepted by the request, `null` will be returned:
+
+ $preferred = $request->prefers(['text/html', 'application/json']);
+
+Since many applications only serve HTML or JSON, you may use the `expectsJson` method to quickly determine if the incoming request expects a JSON response:
+
+ if ($request->expectsJson()) {
+ // ...
+ }
+
### PSR-7 Requests
@@ -130,7 +198,7 @@ The [PSR-7 standard](https://www.php-fig.org/psr/psr-7/) specifies interfaces fo
composer require symfony/psr-http-message-bridge
composer require nyholm/psr7
-Once you have installed these libraries, you may obtain a PSR-7 request by type-hinting the request interface on your route Closure or controller method:
+Once you have installed these libraries, you may obtain a PSR-7 request by type-hinting the request interface on your route closure or controller method:
use Psr\Http\Message\ServerRequestInterface;
@@ -140,20 +208,16 @@ Once you have installed these libraries, you may obtain a PSR-7 request by type-
> {tip} If you return a PSR-7 response instance from a route or controller, it will automatically be converted back to a Laravel response instance and be displayed by the framework.
-
-## Input Trimming & Normalization
-
-By default, Laravel includes the `TrimStrings` and `ConvertEmptyStringsToNull` middleware in your application's global middleware stack. These middleware are listed in the stack by the `App\Http\Kernel` class. These middleware will automatically trim all incoming string fields on the request, as well as convert any empty string fields to `null`. This allows you to not have to worry about these normalization concerns in your routes and controllers.
-
-If you would like to disable this behavior, you may remove the two middleware from your application's middleware stack by removing them from the `$middleware` property of your `App\Http\Kernel` class.
+
+## Input
-## Retrieving Input
+### Retrieving Input
#### Retrieving All Input Data
-You may also retrieve all of the input data as an `array` using the `all` method:
+You may retrieve all of the incoming request's input data as an `array` using the `all` method. This method may be used regardless of whether the incoming request is from an HTML form or is an XHR request:
$input = $request->all();
@@ -193,19 +257,10 @@ You may call the `query` method without any arguments in order to retrieve all o
$query = $request->query();
-
-#### Retrieving Input Via Dynamic Properties
-
-You may also access user input using dynamic properties on the `Illuminate\Http\Request` instance. For example, if one of your application's forms contains a `name` field, you may access the value of the field like so:
-
- $name = $request->name;
-
-When using dynamic properties, Laravel will first look for the parameter's value in the request payload. If it is not present, Laravel will search for the field in the route parameters.
-
#### Retrieving JSON Input Values
-When sending JSON requests to your application, you may access the JSON data via the `input` method as long as the `Content-Type` header of the request is properly set to `application/json`. You may even use "dot" syntax to dig into JSON arrays:
+When sending JSON requests to your application, you may access the JSON data via the `input` method as long as the `Content-Type` header of the request is properly set to `application/json`. You may even use "dot" syntax to retrieve values that are nested within JSON arrays:
$name = $request->input('user.name');
@@ -216,6 +271,15 @@ When dealing with HTML elements like checkboxes, your application may receive "t
$archived = $request->boolean('archived');
+
+#### Retrieving Input Via Dynamic Properties
+
+You may also access user input using dynamic properties on the `Illuminate\Http\Request` instance. For example, if one of your application's forms contains a `name` field, you may access the value of the field like so:
+
+ $name = $request->name;
+
+When using dynamic properties, Laravel will first look for the parameter's value in the request payload. If it is not present, Laravel will search for the field in the matched route's parameters.
+
#### Retrieving A Portion Of The Input Data
@@ -229,12 +293,12 @@ If you need to retrieve a subset of the input data, you may use the `only` and `
$input = $request->except('credit_card');
-> {tip} The `only` method returns all of the key / value pairs that you request; however, it will not return key / value pairs that are not present on the request.
+> {note} The `only` method returns all of the key / value pairs that you request; however, it will not return key / value pairs that are not present on the request.
-
-#### Determining If An Input Value Is Present
+
+### Determining If Input Is Present
-You should use the `has` method to determine if a value is present on the request. The `has` method returns `true` if the value is present on the request:
+You may use the `has` method to determine if a value is present on the request. The `has` method returns `true` if the value is present on the request:
if ($request->has('name')) {
//
@@ -246,7 +310,7 @@ When given an array, the `has` method will determine if all of the specified val
//
}
-The `whenHas` method will execute the given callback if a value is present on the request:
+The `whenHas` method will execute the given closure if a value is present on the request:
$request->whenHas('name', function ($input) {
//
@@ -264,7 +328,7 @@ If you would like to determine if a value is present on the request and is not e
//
}
-The `whenFilled` method will execute the given callback if a value is present on the request and is not empty:
+The `whenFilled` method will execute the given closure if a value is present on the request and is not empty:
$request->whenFilled('name', function ($input) {
//
@@ -279,7 +343,7 @@ To determine if a given key is absent from the request, you may use the `missing
### Old Input
-Laravel allows you to keep input from one request during the next request. This feature is particularly useful for re-populating forms after detecting validation errors. However, if you are using Laravel's included [validation features](/docs/{{version}}/validation), it is unlikely you will need to manually use these methods, as some of Laravel's built-in validation facilities will call them automatically.
+Laravel allows you to keep input from one request during the next request. This feature is particularly useful for re-populating forms after detecting validation errors. However, if you are using Laravel's included [validation features](/docs/{{version}}/validation), it is possible that you will not need to manually use these session input flashing methods directly, as some of Laravel's built-in validation facilities will call them automatically.
#### Flashing Input To The Session
@@ -301,6 +365,8 @@ Since you often will want to flash input to the session and then redirect to the
return redirect('form')->withInput();
+ return redirect()->route('user.create')->withInput();
+
return redirect('form')->withInput(
$request->except('password')
);
@@ -308,11 +374,11 @@ Since you often will want to flash input to the session and then redirect to the
#### Retrieving Old Input
-To retrieve flashed input from the previous request, use the `old` method on the `Request` instance. The `old` method will pull the previously flashed input data from the [session](/docs/{{version}}/session):
+To retrieve flashed input from the previous request, invoke the `old` method on an instance of `Illuminate\Http\Request`. The `old` method will pull the previously flashed input data from the [session](/docs/{{version}}/session):
$username = $request->old('username');
-Laravel also provides a global `old` helper. If you are displaying old input within a [Blade template](/docs/{{version}}/blade), it is more convenient to use the `old` helper. If no old input exists for the given field, `null` will be returned:
+Laravel also provides a global `old` helper. If you are displaying old input within a [Blade template](/docs/{{version}}/blade), it is more convenient to use the `old` helper to repopulate the form. If no old input exists for the given field, `null` will be returned:
@@ -326,54 +392,12 @@ All cookies created by the Laravel framework are encrypted and signed with an au
$value = $request->cookie('name');
-Alternatively, you may use the `Cookie` facade to access cookie values:
-
- use Illuminate\Support\Facades\Cookie;
-
- $value = Cookie::get('name');
-
-
-#### Attaching Cookies To Responses
-
-You may attach a cookie to an outgoing `Illuminate\Http\Response` instance using the `cookie` method. You should pass the name, value, and number of minutes the cookie should be considered valid to this method:
-
- return response('Hello World')->cookie(
- 'name', 'value', $minutes
- );
-
-The `cookie` method also accepts a few more arguments which are used less frequently. Generally, these arguments have the same purpose and meaning as the arguments that would be given to PHP's native [setcookie](https://secure.php.net/manual/en/function.setcookie.php) method:
-
- return response('Hello World')->cookie(
- 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly
- );
-
-Alternatively, you can use the `Cookie` facade to "queue" cookies for attachment to the outgoing response from your application. The `queue` method accepts a `Cookie` instance or the arguments needed to create a `Cookie` instance. These cookies will be attached to the outgoing response before it is sent to the browser:
-
- Cookie::queue(Cookie::make('name', 'value', $minutes));
-
- Cookie::queue('name', 'value', $minutes);
-
-
-#### Generating Cookie Instances
-
-If you would like to generate a `Symfony\Component\HttpFoundation\Cookie` instance that can be given to a response instance at a later time, you may use the global `cookie` helper. This cookie will not be sent back to the client unless it is attached to a response instance:
-
- $cookie = cookie('name', 'value', $minutes);
-
- return response('Hello World')->cookie($cookie);
-
-
-#### Expiring Cookies Early
-
-You may remove a cookie by expiring it via the `forget` method of the `Cookie` facade:
-
- Cookie::queue(Cookie::forget('name'));
-
-Alternatively, you may attach the expired cookie to a response instance:
+
+## Input Trimming & Normalization
- $cookie = Cookie::forget('name');
+By default, Laravel includes the `App\Http\Middleware\TrimStrings` and `App\Http\Middleware\ConvertEmptyStringsToNull` middleware in your application's global middleware stack. These middleware are listed in the global middleware stack by the `App\Http\Kernel` class. These middleware will automatically trim all incoming string fields on the request, as well as convert any empty string fields to `null`. This allows you to not have to worry about these normalization concerns in your routes and controllers.
- return response('Hello World')->withCookie($cookie);
+If you would like to disable this behavior, you may remove the two middleware from your application's middleware stack by removing them from the `$middleware` property of your `App\Http\Kernel` class.
## Files
@@ -381,7 +405,7 @@ Alternatively, you may attach the expired cookie to a response instance:
### Retrieving Uploaded Files
-You may access uploaded files from an `Illuminate\Http\Request` instance using the `file` method or using dynamic properties. The `file` method returns an instance of the `Illuminate\Http\UploadedFile` class, which extends the PHP `SplFileInfo` class and provides a variety of methods for interacting with the file:
+You may retrieve uploaded files from an `Illuminate\Http\Request` instance using the `file` method or using dynamic properties. The `file` method returns an instance of the `Illuminate\Http\UploadedFile` class, which extends the PHP `SplFileInfo` class and provides a variety of methods for interacting with the file:
$file = $request->file('photo');
@@ -419,9 +443,9 @@ There are a variety of other methods available on `UploadedFile` instances. Chec
### Storing Uploaded Files
-To store an uploaded file, you will typically use one of your configured [filesystems](/docs/{{version}}/filesystem). The `UploadedFile` class has a `store` method which will move an uploaded file to one of your disks, which may be a location on your local filesystem or even a cloud storage location like Amazon S3.
+To store an uploaded file, you will typically use one of your configured [filesystems](/docs/{{version}}/filesystem). The `UploadedFile` class has a `store` method that will move an uploaded file to one of your disks, which may be a location on your local filesystem or a cloud storage location like Amazon S3.
-The `store` method accepts the path where the file should be stored relative to the filesystem's configured root directory. This path should not contain a file name, since a unique ID will automatically be generated to serve as the file name.
+The `store` method accepts the path where the file should be stored relative to the filesystem's configured root directory. This path should not contain a filename, since a unique ID will automatically be generated to serve as the filename.
The `store` method also accepts an optional second argument for the name of the disk that should be used to store the file. The method will return the path of the file relative to the disk's root:
@@ -429,16 +453,18 @@ The `store` method also accepts an optional second argument for the name of the
$path = $request->photo->store('images', 's3');
-If you do not want a file name to be automatically generated, you may use the `storeAs` method, which accepts the path, file name, and disk name as its arguments:
+If you do not want a filename to be automatically generated, you may use the `storeAs` method, which accepts the path, filename, and disk name as its arguments:
$path = $request->photo->storeAs('images', 'filename.jpg');
$path = $request->photo->storeAs('images', 'filename.jpg', 's3');
+> {tip} For more information about file storage in Laravel, check out the complete [file storage documentation](/docs/{{version}}/filesystem).
+
## Configuring Trusted Proxies
-When running your applications behind a load balancer that terminates TLS / SSL certificates, you may notice your application sometimes does not generate HTTPS links. Typically this is because your application is being forwarded traffic from your load balancer on port 80 and does not know it should generate secure links.
+When running your applications behind a load balancer that terminates TLS / SSL certificates, you may notice your application sometimes does not generate HTTPS links when using the `url` helper. Typically this is because your application is being forwarded traffic from your load balancer on port 80 and does not know it should generate secure links.
To solve this, you may use the `App\Http\Middleware\TrustProxies` middleware that is included in your Laravel application, which allows you to quickly customize the load balancers or proxies that should be trusted by your application. Your trusted proxies should be listed as an array on the `$proxies` property of this middleware. In addition to configuring the trusted proxies, you may configure the proxy `$headers` that should be trusted:
diff --git a/responses.md b/responses.md
index e96ba0db311..88f7c33cdbb 100644
--- a/responses.md
+++ b/responses.md
@@ -43,13 +43,24 @@ Typically, you won't just be returning simple strings or arrays from your route
Returning a full `Response` instance allows you to customize the response's HTTP status code and headers. A `Response` instance inherits from the `Symfony\Component\HttpFoundation\Response` class, which provides a variety of methods for building HTTP responses:
- Route::get('home', function () {
+ Route::get('/home', function () {
return response('Hello World', 200)
->header('Content-Type', 'text/plain');
});
+
+#### Eloquent Models & Collections
+
+You may also return [Eloquent ORM](/docs/{{version}}/eloquent) models and collections directly from your routes and controllers. When you do, Laravel will automatically convert the models and collections to JSON responses while respecting the model's [hidden attributes](/docs/{{version}}/eloquent-serialization#hiding-attributes-from-json):
+
+ use App\Models\User;
+
+ Route::get('/user/{user}', function (User $user) {
+ return $user;
+ });
+
-#### Attaching Headers To Responses
+### Attaching Headers To Responses
Keep in mind that most response methods are chainable, allowing for the fluent construction of response instances. For example, you may use the `header` method to add a series of headers to the response before sending it back to the user:
@@ -73,36 +84,58 @@ Or, you may use the `withHeaders` method to specify an array of headers to be ad
Laravel includes a `cache.headers` middleware, which may be used to quickly set the `Cache-Control` header for a group of routes. If `etag` is specified in the list of directives, an MD5 hash of the response content will automatically be set as the ETag identifier:
Route::middleware('cache.headers:public;max_age=2628000;etag')->group(function () {
- Route::get('privacy', function () {
+ Route::get('/privacy', function () {
// ...
});
- Route::get('terms', function () {
+ Route::get('/terms', function () {
// ...
});
});
-#### Attaching Cookies To Responses
+### Attaching Cookies To Responses
-The `cookie` method on response instances allows you to easily attach cookies to the response. For example, you may use the `cookie` method to generate a cookie and fluently attach it to the response instance like so:
+You may attach a cookie to an outgoing `Illuminate\Http\Response` instance using the `cookie` method. You should pass the name, value, and number of minutes the cookie should be considered valid to this method:
- return response($content)
- ->header('Content-Type', $type)
- ->cookie('name', 'value', $minutes);
+ return response('Hello World')->cookie(
+ 'name', 'value', $minutes
+ );
The `cookie` method also accepts a few more arguments which are used less frequently. Generally, these arguments have the same purpose and meaning as the arguments that would be given to PHP's native [setcookie](https://secure.php.net/manual/en/function.setcookie.php) method:
- ->cookie($name, $value, $minutes, $path, $domain, $secure, $httpOnly)
+ return response('Hello World')->cookie(
+ 'name', 'value', $minutes, $path, $domain, $secure, $httpOnly
+ );
-Alternatively, you can use the `Cookie` facade to "queue" cookies for attachment to the outgoing response from your application. The `queue` method accepts a `Cookie` instance or the arguments needed to create a `Cookie` instance. These cookies will be attached to the outgoing response before it is sent to the browser:
+If you would like to ensure that a cookie is sent with the outgoing response but you do not yet have an instance of that response, you can use the `Cookie` facade to "queue" cookies for attachment to the response when it is sent. The `queue` method accepts the arguments needed to create a cookie instance. These cookies will be attached to the outgoing response before it is sent to the browser:
- Cookie::queue(Cookie::make('name', 'value', $minutes));
+ use Illuminate\Support\Facades\Cookie;
Cookie::queue('name', 'value', $minutes);
+
+#### Generating Cookie Instances
+
+If you would like to generate a `Symfony\Component\HttpFoundation\Cookie` instance that can be attached to a response instance at a later time, you may use the global `cookie` helper. This cookie will not be sent back to the client unless it is attached to a response instance:
+
+ $cookie = cookie('name', 'value', $minutes);
+
+ return response('Hello World')->cookie($cookie);
+
+
+#### Expiring Cookies Early
+
+You may remove a cookie by expiring it via the `withoutCookie` method of an outgoing response:
+
+ return response('Hello World')->withoutCookie('name');
+
+If you do not yet have an instance of the outgoing response, you may use the `Cookie` facade's `queue` method to expire a cookie:
+
+ Cookie::queue(Cookie::forget('name'));
+
-#### Cookies & Encryption
+### Cookies & Encryption
By default, all cookies generated by Laravel are encrypted and signed so that they can't be modified or read by the client. If you would like to disable encryption for a subset of cookies generated by your application, you may use the `$except` property of the `App\Http\Middleware\EncryptCookies` middleware, which is located in the `app/Http/Middleware` directory:
@@ -120,13 +153,13 @@ By default, all cookies generated by Laravel are encrypted and signed so that th
Redirect responses are instances of the `Illuminate\Http\RedirectResponse` class, and contain the proper headers needed to redirect the user to another URL. There are several ways to generate a `RedirectResponse` instance. The simplest method is to use the global `redirect` helper:
- Route::get('dashboard', function () {
+ Route::get('/dashboard', function () {
return redirect('home/dashboard');
});
-Sometimes you may wish to redirect the user to their previous location, such as when a submitted form is invalid. You may do so by using the global `back` helper function. Since this feature utilizes the [session](/docs/{{version}}/session), make sure the route calling the `back` function is using the `web` middleware group or has all of the session middleware applied:
+Sometimes you may wish to redirect the user to their previous location, such as when a submitted form is invalid. You may do so by using the global `back` helper function. Since this feature utilizes the [session](/docs/{{version}}/session), make sure the route calling the `back` function is using the `web` middleware group:
- Route::post('user/profile', function () {
+ Route::post('/user/profile', function () {
// Validate the request...
return back()->withInput();
@@ -141,7 +174,7 @@ When you call the `redirect` helper with no parameters, an instance of `Illumina
If your route has parameters, you may pass them as the second argument to the `route` method:
- // For a route with the following URI: profile/{id}
+ // For a route with the following URI: /profile/{id}
return redirect()->route('profile', ['id' => 1]);
@@ -150,11 +183,11 @@ If your route has parameters, you may pass them as the second argument to the `r
If you are redirecting to a route with an "ID" parameter that is being populated from an Eloquent model, you may pass the model itself. The ID will be extracted automatically:
- // For a route with the following URI: profile/{id}
+ // For a route with the following URI: /profile/{id}
return redirect()->route('profile', [$user]);
-If you would like to customize the value that is placed in the route parameter, you can specify the column in the route parameter definition (`profile/{id:slug}`) or you can override the `getRouteKey` method on your Eloquent model:
+If you would like to customize the value that is placed in the route parameter, you can specify the column in the route parameter definition (`/profile/{id:slug}`) or you can override the `getRouteKey` method on your Eloquent model:
/**
* Get the value of the model's route key.
@@ -171,9 +204,9 @@ If you would like to customize the value that is placed in the route parameter,
You may also generate redirects to [controller actions](/docs/{{version}}/controllers). To do so, pass the controller and action name to the `action` method:
- use App\Http\Controllers\HomeController;
+ use App\Http\Controllers\UserController;
- return redirect()->action([HomeController::class, 'index']);
+ return redirect()->action([UserController::class, 'index']);
If your controller route requires parameters, you may pass them as the second argument to the `action` method:
@@ -193,8 +226,8 @@ Sometimes you may need to redirect to a domain outside of your application. You
Redirecting to a new URL and [flashing data to the session](/docs/{{version}}/session#flash-data) are usually done at the same time. Typically, this is done after successfully performing an action when you flash a success message to the session. For convenience, you may create a `RedirectResponse` instance and flash data to the session in a single, fluent method chain:
- Route::post('user/profile', function () {
- // Update the user's profile...
+ Route::post('/user/profile', function () {
+ // ...
return redirect('dashboard')->with('status', 'Profile updated!');
});
@@ -207,6 +240,13 @@ After the user is redirected, you may display the flashed message from the [sess
@endif
+
+#### Redirecting With Input
+
+You may use the `withInput` method provided by the `RedirectResponse` instance to flash the current request's input data to the session before redirecting the user to a new location. This is typically done if the user has encountered a validation error. Once the input has been flashed to the session, you may easily [retrieve it](/docs/{{version}}/requests#retrieving-old-input) during the next request to repopulate the form:
+
+ return back()->withInput();
+
## Other Response Types
@@ -221,7 +261,7 @@ If you need control over the response's status and headers but also need to retu
->view('hello', $data, 200)
->header('Content-Type', $type);
-Of course, if you do not need to pass a custom HTTP status code or custom headers, you should use the global `view` helper function.
+Of course, if you do not need to pass a custom HTTP status code or custom headers, you may use the global `view` helper function.
### JSON Responses
@@ -242,20 +282,20 @@ If you would like to create a JSONP response, you may use the `json` method in c
### 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:
+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 filename as the second argument to the method, which will determine the filename 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);
return response()->download($pathToFile, $name, $headers);
- return response()->download($pathToFile)->deleteFileAfterSend();
-
-> {note} Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII file name.
+> {note} Symfony HttpFoundation, which manages file downloads, requires the file being downloaded to have an ASCII filename.
#### Streamed Downloads
-Sometimes you may wish to turn the string response of a given operation into a downloadable response without having to write the contents of the operation to disk. You may use the `streamDownload` method in this scenario. This method accepts a callback, file name, and an optional array of headers as its arguments:
+Sometimes you may wish to turn the string response of a given operation into a downloadable response without having to write the contents of the operation to disk. You may use the `streamDownload` method in this scenario. This method accepts a callback, filename, and an optional array of headers as its arguments:
+
+ use App\Services\GitHub;
return response()->streamDownload(function () {
echo GitHub::api('repo')
@@ -275,7 +315,7 @@ The `file` method may be used to display a file, such as an image or PDF, direct
## 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 `macro` method on the `Response` facade. For example, from a [service provider's](/docs/{{version}}/providers) `boot` method:
+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 `macro` method on the `Response` facade. Typically, you should call this method from the `boot` method of one of your application's [service providers](/docs/{{version}}/providers), such as the `App\Providers\AppProvider` service provider:
caps('foo');
diff --git a/routing.md b/routing.md
index 46a48ad211b..cadf5455158 100644
--- a/routing.md
+++ b/routing.md
@@ -23,22 +23,25 @@
- [Form Method Spoofing](#form-method-spoofing)
- [Accessing The Current Route](#accessing-the-current-route)
- [Cross-Origin Resource Sharing (CORS)](#cors)
+- [Route Caching](#route-caching)
## Basic Routing
-The most basic Laravel routes accept a URI and a `Closure`, providing a very simple and expressive method of defining routes:
+The most basic Laravel routes accept a URI and a closure, providing a very simple and expressive method of defining routes and behavior without complicated routing configuration files:
- Route::get('foo', function () {
+ use Illuminate\Support\Facades\Route;
+
+ Route::get('/greeting', function () {
return 'Hello World';
});
#### The Default Route Files
-All Laravel routes are defined in your route files, which are located in the `routes` directory. These files are automatically loaded by the framework. The `routes/web.php` file defines routes that are for your web interface. These routes are assigned the `web` middleware group, which provides features like session state and CSRF protection. The routes in `routes/api.php` are stateless and are assigned the `api` middleware group.
+All Laravel routes are defined in your route files, which are located in the `routes` directory. These files are automatically loaded by your application's `App\Providers\RouteServiceProvider`. The `routes/web.php` file defines routes that are for your web interface. These routes are assigned the `web` middleware group, which provides features like session state and CSRF protection. The routes in `routes/api.php` are stateless and are assigned the `api` middleware group.
-For most applications, you will begin by defining routes in your `routes/web.php` file. The routes defined in `routes/web.php` may be accessed by entering the defined route's URL in your browser. For example, you may access the following route by navigating to `http://your-app.test/user` in your browser:
+For most applications, you will begin by defining routes in your `routes/web.php` file. The routes defined in `routes/web.php` may be accessed by entering the defined route's URL in your browser. For example, you may access the following route by navigating to `http://example.com/user` in your browser:
use App\Http\Controllers\UserController;
@@ -68,10 +71,21 @@ Sometimes you may need to register a route that responds to multiple HTTP verbs.
//
});
+
+#### Dependency Injection
+
+You may type-hint any dependencies required by your route in your route's callback signature. The declared dependencies will automatically be resolved and injected into the callback by the Laravel [service container](/docs/{{version}}/container). For example, you may type-hint the `Illuminate\Http\Request` class to have the current HTTP request automatically injected into your route callback:
+
+ use Illuminate\Http\Request;
+
+ Route::get('/users', function (Request $request) {
+ // ...
+ });
+
#### CSRF Protection
-Any HTML forms pointing to `POST`, `PUT`, `PATCH`, or `DELETE` routes that are defined in the `web` routes file should include a CSRF token field. Otherwise, the request will be rejected. You can read more about CSRF protection in the [CSRF documentation](/docs/{{version}}/csrf):
+Remember, any HTML forms pointing to `POST`, `PUT`, `PATCH`, or `DELETE` routes that are defined in the `web` routes file should include a CSRF token field. Otherwise, the request will be rejected. You can read more about CSRF protection in the [CSRF documentation](/docs/{{version}}/csrf):
-You may use the `@method` Blade directive to generate the `_method` input:
+For convenience, you may use the `@method` [Blade directive](/docs/{{version}}/blade) to generate the `_method` input field:
-
@@ -578,17 +628,30 @@ You may use the `@method` Blade directive to generate the `_method` input:
You may use the `current`, `currentRouteName`, and `currentRouteAction` methods on the `Route` facade to access information about the route handling the incoming request:
- $route = Route::current();
+ use Illuminate\Support\Facades\Route;
- $name = Route::currentRouteName();
+ $route = Route::current(); // Illuminate\Routing\Route
+ $name = Route::currentRouteName(); // string
+ $action = Route::currentRouteAction(); // string
- $action = Route::currentRouteAction();
-
-Refer to the API documentation for both the [underlying class of the Route facade](https://laravel.com/api/{{version}}/Illuminate/Routing/Router.html) and [Route instance](https://laravel.com/api/{{version}}/Illuminate/Routing/Route.html) to review all accessible methods.
+You may refer to the API documentation for both the [underlying class of the Route facade](https://laravel.com/api/{{version}}/Illuminate/Routing/Router.html) and [Route instance](https://laravel.com/api/{{version}}/Illuminate/Routing/Route.html) to review all of the methods that are available on the router and route classes.
## Cross-Origin Resource Sharing (CORS)
-Laravel can automatically respond to CORS OPTIONS requests with values that you configure. All CORS settings may be configured in your `cors` configuration file and OPTIONS requests will automatically be handled by the `HandleCors` middleware that is included by default in your global middleware stack.
+Laravel can automatically respond to CORS `OPTIONS` HTTP requests with values that you configure. All CORS settings may be configured in your application's `config/cors.php` configuration file. The `OPTIONS` requests will automatically be handled by the `HandleCors` [middleware](/docs/{{version}}/middleware) that is included by default in your global middleware stack. Your global middleware stack is located in your application's HTTP kernel (`App\Http\Kernel`).
> {tip} For more information on CORS and CORS headers, please consult the [MDN web documentation on CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#The_HTTP_response_headers).
+
+
+## Route Caching
+
+When deploying your application to production, you should take advantage of Laravel's route cache. Using the route cache will drastically decrease the amount of time it takes to register all of your application's routes. To generate a route cache, execute the `route:cache` Artisan command:
+
+ php artisan route:cache
+
+After running this command, your cached routes file will be loaded on every request. Remember, if you add any new routes you will need to generate a fresh route cache. Because of this, you should only run the `route:cache` command during your project's deployment.
+
+You may use the `route:clear` command to clear the route cache:
+
+ php artisan route:clear
diff --git a/sail.md b/sail.md
new file mode 100644
index 00000000000..90f10427624
--- /dev/null
+++ b/sail.md
@@ -0,0 +1,254 @@
+# Laravel Sail
+
+- [Introduction](#introduction)
+- [Installation & Setup](#installation)
+ - [Configuring A Bash Alias](#configuring-a-bash-alias)
+- [Starting & Stopping Sail](#starting-and-stopping-sail)
+- [Executing Commands](#executing-sail-commands)
+ - [Executing PHP Commands](#executing-php-commands)
+ - [Executing Composer Commands](#executing-composer-commands)
+ - [Executing Artisan Commands](#executing-artisan-commands)
+ - [Executing Node / NPM Commands](#executing-node-npm-commands)
+- [Interacting With Databases](#interacting-with-sail-databases)
+ - [MySQL](#mysql)
+ - [Redis](#redis)
+- [Running Tests](#running-tests)
+ - [Laravel Dusk](#laravel-dusk)
+- [Previewing Emails](#previewing-emails)
+- [Container CLI](#sail-container-cli)
+- [PHP Versions](#sail-php-versions)
+- [Customization](#sail-customization)
+
+
+## Introduction
+
+Laravel Sail is a light-weight command-line interface for interacting with Laravel's default Docker development environment. Sail provides a great starting point for building a Laravel application using PHP, MySQL, and Redis without requiring prior Docker experience.
+
+At its heart, Sail is the `docker-compose.yml` file and the `sail` script that is stored at the root of your project. The `sail` script provides a CLI with convenient methods for interacting with the Docker containers defined by the `docker-compose.yml` file.
+
+Laravel Sail is supported on macOS, Linux, and Windows (via WSL2).
+
+
+## Installation & Setup
+
+Laravel Sail is automatically installed with all new Laravel applications so you may start using it immediately. To learn how to create a new Laravel application, please consult Laravel's [installation documentation](/docs/{{version}}/installation) for your operating system.
+
+
+### Configuring A Bash Alias
+
+By default, Sail commands are invoked using the `vendor/bin/sail` script that is included with all new Laravel applications:
+
+```bash
+./vendor/bin/sail up
+```
+
+However, instead of repeatedly typing `vendor/bin/sail` to execute Sail commands, you may wish to configure a Bash alias that allows you to execute Sail's commands more easily:
+
+```bash
+alias sail='bash vendor/bin/sail'
+```
+
+Once the Bash alias has been configured, you may execute Sail commands by simply typing `sail`. The remainder of this documentation's examples will assume that you have configured this alias:
+
+```bash
+sail up
+```
+
+
+## Starting & Stopping Sail
+
+Laravel Sail's `docker-compose.yml` file defines a Docker variety of containers that work together to help you build Laravel applications. Each of these containers is an entry within the `services` configuration of your `docker-compose.yml` file. The `laravel.test` container is the primary application container that will be serving your application.
+
+Before staring Sail, you should ensure that no other web servers or databases are running on your local computer. To start all of the Docker containers defined in your application's `docker-compose.yml` file, you should execute the `up` command:
+
+```bash
+sail up
+```
+
+To start all of the Docker containers in the background, you may start Sail in "detached" mode:
+
+```bash
+sail up -d
+```
+
+Once the application's containers have been started, you may access the project in your web browser at: http://localhost.
+
+To stop all of the containers, you may simply press Control + C to stop the container's execution. Or, if the containers are running in the background, you may use the `down` command:
+
+```bash
+sail down
+```
+
+
+## Executing Commands
+
+When using Laravel Sail, your application is executing within a Docker container and is isolated from your local computer. However, Sail provides a convenient way to run various commands against your application such as arbitrary PHP commands, Artisan commands, Composer commands, and Node / NPM commands.
+
+**When reading the Laravel documentation, you will often see references to Composer, Artisan, and Node / NPM commands that do not reference Sail.** Those examples assume that these tools are installed on your local computer. If you are using Sail for your local Laravel development environment, you should execute those commands using Sail:
+
+```bash
+# Running Artisan commands locally...
+php artisan queue:work
+
+# Running Artisan commands within Laravel Sail...
+sail artisan queue:work
+```
+
+
+### Executing PHP Commands
+
+PHP commands may be executed using the `php` command. Of course, these commands will execute using the PHP version that is configured for your application. To learn more about the PHP versions available to Laravel Sail, consult the [PHP version documentation](#sail-php-versions):
+
+```bash
+sail php --version
+
+sail php script.php
+```
+
+
+### Executing Composer Commands
+
+Composer commands may be executed using the `composer` command. Laravel Sail's application container includes a Composer 2.x installation:
+
+```nothing
+sail composer require laravel/sanctum
+```
+
+
+### Executing Artisan Commands
+
+Laravel Artisan commands may be executed using the `artisan` command:
+
+```bash
+sail artisan queue:work
+```
+
+
+### Executing Node / NPM Commands
+
+Node commands may be executed using the `npm` command while NPM commands may be executed using the `npm` command:
+
+```nothing
+sail node --version
+
+sail npm run prod
+```
+
+
+## Interacting With Databases
+
+
+### MySQL
+
+As you may have noticed, your application's `docker-compose.yml` file contains an entry for a MySQL container. This container uses a [Docker volume](https://docs.docker.com/storage/volumes/) so that the data stored in your database is persisted even when stopping and restarting your containers. Once you have started your containers, you may connect to the MySQL instance within your application by setting your `DB_HOST` environment variable within your application's `.env` file to `mysql`.
+
+To connect to your application's MySQL database from your local machine, you may use a graphical database management application such as [TablePlus](https://tableplus.com). By default, the MySQL database is accessible at `localhost` port 3306.
+
+
+### Redis
+
+Your application's `docker-compose.yml` file also contains an entry for a [Redis](https://redis.io) container. This container uses a [Docker volume](https://docs.docker.com/storage/volumes/) so that the data stored in your Redis data is persisted even when stopping and restarting your containers. Once you have started your containers, you may connect to the Redis instance within your application by setting your `REDIS_HOST` environment variable within your application's `.env` file to `redis`.
+
+To connect to your application's Redis database from your local machine, you may use a graphical database management application such as [TablePlus](https://tableplus.com). By default, the Redis database is accessible at `localhost` port 6379.
+
+
+## Running Tests
+
+Laravel provides amazing testing support out of the box, and you may use Sail's `test` command to run your applications [feature and unit tests](/docs/{{version}}/testing). Any CLI options that are accepted by PHPUnit may also be passed to the `test` command:
+
+ sail test
+
+ sail test --group orders
+
+The Sail `test` command is equivalent to running the `test` Artisan command:
+
+ sail artisan test
+
+
+### Laravel Dusk
+
+[Laravel Dusk](/docs/{{version}}/dusk) provides an expressive, easy-to-use browser automation and testing API. Thanks to Sail, you may run these tests without ever installing Selenium or other tools on your local computer. To get started, uncomment the Selenium service in your application's `docker-compose.yml` file:
+
+ selenium:
+ image: 'selenium/standalone-chrome'
+ volumes:
+ - '/dev/shm:/dev/shm'
+ networks:
+ - sail
+
+Next, ensure that the `laravel.test` service in your application's `docker-compose.yml` file has a `depends_on` entry for `selenium`:
+
+ depends_on:
+ - mysql
+ - redis
+ - selenium
+
+Finally, you may run your Dusk test suite by starting Sail and running the `dusk` command:
+
+ sail dusk
+
+
+## Previewing Emails
+
+Laravel Sail's default `docker-compose.yml` file contains a service entry for [MailHog](https://github.com/mailhog/MailHog). MailHog intercepts emails sent by your application during local development and provides a convenient web interface so that you can preview your email messages in your browser. MailHog's default SMTP port is `1025`:
+
+```bash
+MAIL_PORT=1025
+```
+
+When Sail is running, you may access the MailHog web interface at: http://localhost:8025
+
+
+## Container CLI
+
+Sometimes you may wish to start a Bash session within your application's container. You may use the `ssh` command to connect to your application's container, allowing you to inspect its files and installed services as well execute arbitrary shell commands within the container:
+
+```nothing
+sail ssh
+```
+
+To start a new [Laravel Tinker](https://github.com/laravel/tinker) session, you may execute the `tinker` command:
+
+```bash
+sail tinker
+```
+
+
+## PHP Versions
+
+Sail currently supports serving your application via PHP 8.0 or PHP 7.4. To change the PHP version that is used to serve your application, you should update the `build` definition of the `laravel.test` container in your application's `docker-compose.yml` file:
+
+```yaml
+# PHP 8.0
+context: ./vendor/laravel/sail/runtimes/8.0
+
+# PHP 7.4
+context: ./vendor/laravel/sail/runtimes/7.4
+```
+
+In addition, you may wish to update your `image` name to reflect the version of PHP being used by your application. This option is also defined in your application's `docker-compose.yml` file:
+
+```yaml
+image: sail-8.0/app
+```
+
+After updating your application's `docker-compose.yml` file, you should rebuild your container images:
+
+ sail build --no-cache
+
+ sail up
+
+
+## Sail Customization
+
+Since Sail is just Docker, you are free to customize nearly everything about it. To publish Sail's own Dockerfiles, you may execute the `sail:publish` command:
+
+```bash
+sail artisan sail:publish
+```
+
+After running this command, the Dockerfiles and other configuration files used by Laravel Sail will be placed within a `docker` directory in your application's root directory. After customizing your Sail installation, you may rebuild your application's containers using the `build` command:
+
+```bash
+sail build --no-cache
+```
diff --git a/sanctum.md b/sanctum.md
index 62746981ec7..42636e57db4 100644
--- a/sanctum.md
+++ b/sanctum.md
@@ -29,32 +29,34 @@ Laravel Sanctum provides a featherweight authentication system for SPAs (single
### How It Works
-Laravel Sanctum exists to solve two separate problems.
+Laravel Sanctum exists to solve two separate problems. Let's discuss each before digging deeper into the library.
#### API Tokens
-First, it is a simple package to issue API tokens to your users without the complication of OAuth. This feature is inspired by GitHub "access tokens". For example, imagine the "account settings" of your application has a screen where a user may generate an API token for their account. You may use Sanctum to generate and manage those tokens. These tokens typically have a very long expiration time (years), but may be manually revoked by the user at anytime.
+First, Sanctum is a simple package you may use to issue API tokens to your users without the complication of OAuth. This feature is inspired by GitHub and other applications which issue "personal access tokens". For example, imagine the "account settings" of your application has a screen where a user may generate an API token for their account. You may use Sanctum to generate and manage those tokens. These tokens typically have a very long expiration time (years), but may be manually revoked by the user at anytime.
-Laravel Sanctum offers this feature by storing user API tokens in a single database table and authenticating incoming requests via the `Authorization` header which should contain a valid API token.
+Laravel Sanctum offers this feature by storing user API tokens in a single database table and authenticating incoming HTTP requests via the `Authorization` header which should contain a valid API token.
#### SPA Authentication
-Second, Sanctum exists to offer a simple way to authenticate single page applications (SPAs) that need to communicate with a Laravel powered API. These SPAs might exist in the same repository as your Laravel application or might be an entirely separate repository, such as a SPA created using Vue CLI.
+Second, Sanctum exists to offer a simple way to authenticate single page applications (SPAs) that need to communicate with a Laravel powered API. These SPAs might exist in the same repository as your Laravel application or might be an entirely separate repository, such as a SPA created using Vue CLI or a Next.js application.
-For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses Laravel's built-in cookie based session authentication services. This provides the benefits of CSRF protection, session authentication, as well as protects against leakage of the authentication credentials via XSS. Sanctum will only attempt to authenticate using cookies when the incoming request originates from your own SPA frontend.
+For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses Laravel's built-in cookie based session authentication services. Typically, Sanctum utilizes Laravel's `web` authentication guard to accomplish this. This provides the benefits of CSRF protection, session authentication, as well as protects against leakage of the authentication credentials via XSS.
+
+Sanctum will only attempt to authenticate using cookies when the incoming request originates from your own SPA frontend. When Sanctum examines an incoming HTTP request, it will first check for an authentication cookie and, if none is present, Sanctum will then examine the `Authorization` header for a valid API token.
> {tip} It is perfectly fine to use Sanctum only for API token authentication or only for SPA authentication. Just because you use Sanctum does not mean you are required to use both features it offers.
## Installation
-You may install Laravel Sanctum via Composer:
+You may install Laravel Sanctum via the Composer package manager:
composer require laravel/sanctum
-Next, you should publish the Sanctum configuration and migration files using the `vendor:publish` Artisan command. The `sanctum` configuration file will be placed in your `config` directory:
+Next, you should publish the Sanctum configuration and migration files using the `vendor:publish` Artisan command. The `sanctum` configuration file will be placed in your application's `config` directory:
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
@@ -62,7 +64,7 @@ Finally, you should run your database migrations. Sanctum will create one databa
php artisan migrate
-Next, if you plan to utilize Sanctum to authenticate an SPA, you should add Sanctum's middleware to your `api` middleware group within your `app/Http/Kernel.php` file:
+Next, if you plan to utilize Sanctum to authenticate an SPA, you should add Sanctum's middleware to your `api` middleware group within your application's `app/Http/Kernel.php` file:
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
@@ -73,7 +75,7 @@ Next, if you plan to utilize Sanctum to authenticate an SPA, you should add Sanc
#### Migration Customization
-If you are not going to use Sanctum's default migrations, you should call the `Sanctum::ignoreMigrations` method in the `register` method of your `AppServiceProvider`. You may export the default migrations using `php artisan vendor:publish --tag=sanctum-migrations`.
+If you are not going to use Sanctum's default migrations, you should call the `Sanctum::ignoreMigrations` method in the `register` method of your `App\Providers\AppServiceProvider` class. You may export the default migrations by executing the following command: `php artisan vendor:publish --tag=sanctum-migrations`
## Configuration
@@ -81,7 +83,7 @@ If you are not going to use Sanctum's default migrations, you should call the `S
### Overriding Default Models
-You are free to extend the `PersonalAccessToken` model used internally by Sanctum:
+Although not typically required, you are free to extend the `PersonalAccessToken` model used internally by Sanctum:
use Laravel\Sanctum\PersonalAccessToken as SanctumPersonalAccessToken;
@@ -90,7 +92,7 @@ You are free to extend the `PersonalAccessToken` model used internally by Sanctu
// ...
}
-Then, you may instruct Sanctum to use your custom model via the `usePersonalAccessTokenModel` method provided by Sanctum. Typically, you should call this method in the `boot` method of one of your service providers:
+Then, you may instruct Sanctum to use your custom model via the `usePersonalAccessTokenModel` method provided by Sanctum. Typically, you should call this method in the `boot` method of one of your application's service providers:
use App\Models\Passport\PersonalAccessToken;
use Laravel\Sanctum\Sanctum;
@@ -108,14 +110,14 @@ Then, you may instruct Sanctum to use your custom model via the `usePersonalAcce
## API Token Authentication
-> {tip} You should not use API tokens to authenticate your own first-party SPA. Instead, use Sanctum's built-in [SPA authentication](#spa-authentication).
+> {tip} You should not use API tokens to authenticate your own first-party SPA. Instead, use Sanctum's built-in [SPA authentication features](#spa-authentication).
### Issuing API Tokens
-Sanctum allows you to issue API tokens / personal access tokens that may be used to authenticate API requests. When making requests using API tokens, the token should be included in the `Authorization` header as a `Bearer` token.
+Sanctum allows you to issue API tokens / personal access tokens that may be used to authenticate API requests to your application. When making requests using API tokens, the token should be included in the `Authorization` header as a `Bearer` token.
-To begin issuing tokens for users, your User model should use the `HasApiTokens` trait:
+To begin issuing tokens for users, your User model should use the `Laravel\Sanctum\HasApiTokens` trait:
use Laravel\Sanctum\HasApiTokens;
@@ -126,9 +128,13 @@ To begin issuing tokens for users, your User model should use the `HasApiTokens`
To issue a token, you may use the `createToken` method. The `createToken` method returns a `Laravel\Sanctum\NewAccessToken` instance. API tokens are hashed using SHA-256 hashing before being stored in your database, but you may access the plain-text value of the token using the `plainTextToken` property of the `NewAccessToken` instance. You should display this value to the user immediately after the token has been created:
- $token = $user->createToken('token-name');
+ use Illuminate\Http\Request;
+
+ Route::post('/tokens/create', function (Request $request) {
+ $token = $request->user()->createToken($request->token_name);
- return $token->plainTextToken;
+ return ['token' => $token->plainTextToken];
+ });
You may access all of the user's tokens using the `tokens` Eloquent relationship provided by the `HasApiTokens` trait:
@@ -139,7 +145,7 @@ You may access all of the user's tokens using the `tokens` Eloquent relationship
### Token Abilities
-Sanctum allows you to assign "abilities" to tokens, similar to OAuth "scopes". You may pass an array of string abilities as the second argument to the `createToken` method:
+Sanctum allows you to assign "abilities" to tokens. Abilities serve a similar purpose as OAuth's "scopes". You may pass an array of string abilities as the second argument to the `createToken` method:
return $user->createToken('token-name', ['server:update'])->plainTextToken;
@@ -154,7 +160,11 @@ When handling an incoming request authenticated by Sanctum, you may determine if
### Protecting Routes
-To protect routes so that all incoming requests must be authenticated, you should attach the `sanctum` authentication guard to your API routes within your `routes/api.php` file. This guard will ensure that incoming requests are authenticated as either a stateful authenticated requests from your SPA or contain a valid API token header if the request is from a third party:
+To protect routes so that all incoming requests must be authenticated, you should attach the `sanctum` authentication guard to your protected routes within your `routes/web.php` and `routes/api.php` route files. This guard will ensure that incoming requests are authenticated as either stateful, cookie authenticated requests or contain a valid API token header if the request is from a third party.
+
+You may be wondering why we suggest that you authenticate the routes within your application's `routes/web.php` file using the `sanctum` guard. Remember, Sanctum will first attempt to authenticate incoming requests using Laravel's typical session authentication cookie. If that cookie is not present then Sanctum will attempt to authenticate the request using a token in the request's `Authorization` header. In addition, authenticating all requests using Sanctum ensures that we may always call the `tokenCan` method on the currently authenticated user instance:
+
+ use Illuminate\Http\Request;
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
@@ -163,23 +173,23 @@ To protect routes so that all incoming requests must be authenticated, you shoul
### Revoking Tokens
-You may "revoke" tokens by deleting them from your database using the `tokens` relationship that is provided by the `HasApiTokens` trait:
+You may "revoke" tokens by deleting them from your database using the `tokens` relationship that is provided by the `Laravel\Sanctum\HasApiTokens` trait:
// Revoke all tokens...
$user->tokens()->delete();
- // Revoke the user's current token...
+ // Revoke the token that was used to authenticate the current request...
$request->user()->currentAccessToken()->delete();
// Revoke a specific token...
- $user->tokens()->where('id', $id)->delete();
+ $user->tokens()->where('id', $tokenId)->delete();
## SPA Authentication
-Sanctum exists to offer a simple way to authenticate single page applications (SPAs) that need to communicate with a Laravel powered API. These SPAs might exist in the same repository as your Laravel application or might be an entirely separate repository, such as a SPA created using Vue CLI.
+Sanctum also exists to provide a simple method of authenticating single page applications (SPAs) that need to communicate with a Laravel powered API. These SPAs might exist in the same repository as your Laravel application or might be an entirely separate repository.
-For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses Laravel's built-in cookie based session authentication services. This provides the benefits of CSRF protection, session authentication, as well as protects against leakage of the authentication credentials via XSS. Sanctum will only attempt to authenticate using cookies when the incoming request originates from your own SPA frontend.
+For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses Laravel's built-in cookie based session authentication services. This approach to authentication provides the benefits of CSRF protection, session authentication, as well as protects against leakage of the authentication credentials via XSS.
> {note} In order to authenticate, your SPA and API must share the same top-level domain. However, they may be placed on different subdomains.
@@ -191,7 +201,7 @@ For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses
First, you should configure which domains your SPA will be making requests from. You may configure these domains using the `stateful` configuration option in your `sanctum` configuration file. This configuration setting determines which domains will maintain "stateful" authentication using Laravel session cookies when making requests to your API.
-> {note} If you are accessing your application via a URL that includes the port (`127.0.0.1:8000`), you should ensure that you include the port number with the domain.
+> {note} If you are accessing your application via a URL that includes a port (`127.0.0.1:8000`), you should ensure that you include the port number with the domain.
#### Sanctum Middleware
@@ -211,38 +221,48 @@ Next, you should add Sanctum's middleware to your `api` middleware group within
If you are having trouble authenticating with your application from an SPA that executes on a separate subdomain, you have likely misconfigured your CORS (Cross-Origin Resource Sharing) or session cookie settings.
-You should ensure that your application's CORS configuration is returning the `Access-Control-Allow-Credentials` header with a value of `True` by setting the `supports_credentials` option within your application's `cors` configuration file to `true`.
+You should ensure that your application's CORS configuration is returning the `Access-Control-Allow-Credentials` header with a value of `True`. This may be accomplished by setting the `supports_credentials` option within your application's `config/cors.php` configuration file to `true`.
-In addition, you should enable the `withCredentials` option on your global `axios` instance. Typically, this should be performed in your `resources/js/bootstrap.js` file:
+In addition, you should enable the `withCredentials` option on your application's global `axios` instance. Typically, this should be performed in your `resources/js/bootstrap.js` file. If you are not using Axios to make HTTP requests from your frontend, you should perform the equivalent configuration on your own HTTP client:
axios.defaults.withCredentials = true;
-Finally, you should ensure your application's session cookie domain configuration supports any subdomain of your root domain. You may do this by prefixing the domain with a leading `.` within your `session` configuration file:
+Finally, you should ensure your application's session cookie domain configuration supports any subdomain of your root domain. You may accomplish this by prefixing the domain with a leading `.` within your application's `config/session.php` configuration file:
'domain' => '.domain.com',
### Authenticating
-To authenticate your SPA, your SPA's login page should first make a request to the `/sanctum/csrf-cookie` route to initialize CSRF protection for the application:
+
+#### CSRF Protection
+
+To authenticate your SPA, your SPA's "login: page should first make a request to the `/sanctum/csrf-cookie` endpoint to initialize CSRF protection for the application:
axios.get('/sanctum/csrf-cookie').then(response => {
// Login...
});
-During this request Laravel will set an `XSRF-TOKEN` cookie containing the current CSRF token. This token should then be passed in an `X-XSRF-TOKEN` header on subsequent requests, which libraries like Axios and the Angular HttpClient will do automatically for you.
+During this request Laravel will set an `XSRF-TOKEN` cookie containing the current CSRF token. This token should then be passed in an `X-XSRF-TOKEN` header on subsequent requests, which some HTTP client libraries like Axios and the Angular HttpClient will do automatically for you. If your JavaScript HTTP library does not set the value for you, you will need to manually set the `X-XSRF-TOKEN` header to match the value of the `XSRF-TOKEN` cookie that is set by this route.
+
+
+#### Logging In
+
+Once CSRF protection has been initialized, you should make a `POST` request to the your Laravel application's `/login` route. This `/login` route may be [implemented manually](/docs/{{version}}/authentication#authenticating-users) or using a headless authentication package like [Laravel Fortify](https://github.com/laravel/fortify).
-Once CSRF protection has been initialized, you should make a `POST` request to the typical Laravel `/login` route. This `/login` route may be provided by the `laravel/jetstream` [authentication scaffolding](/docs/{{version}}/authentication#introduction) package.
+If the login request is successful, you will be authenticated and subsequent requests to your application's routes will automatically be authenticated via the session cookie that the Laravel application issued to your client. In addition, since your application already made a request to the `/sanctum/csrf-cookie` route, subsequent requests should automatically receive CSRF protection as long as your JavaScript HTTP client sends the value of the `XSRF-TOKEN` cookie in the `X-XSRF-TOKEN` header.
-If the login request is successful, you will be authenticated and subsequent requests to your API routes will automatically be authenticated via the session cookie that the Laravel backend issued to your client.
+Of course, if your user's session expires due to lack of activity, subsequent requests to the Laravel application may receive 401 or 419 HTTP error response. In this case, you should redirect the user to your SPA's login page.
-> {tip} You are free to write your own `/login` endpoint; however, you should ensure that it authenticates the user using the standard, [session based authentication services that Laravel provides](/docs/{{version}}/authentication#authenticating-users).
+> {note} You are free to write your own `/login` endpoint; however, you should ensure that it authenticates the user using the standard, [session based authentication services that Laravel provides](/docs/{{version}}/authentication#authenticating-users). Typically, this means using the `web` authentication guard.
### Protecting Routes
To protect routes so that all incoming requests must be authenticated, you should attach the `sanctum` authentication guard to your API routes within your `routes/api.php` file. This guard will ensure that incoming requests are authenticated as either a stateful authenticated requests from your SPA or contain a valid API token header if the request is from a third party:
+ use Illuminate\Http\Request;
+
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
@@ -282,12 +302,14 @@ Next, in order for Pusher's authorization requests to succeed, you will need to
## Mobile Application Authentication
-You may use Sanctum tokens to authenticate your mobile application's requests to your API. The process for authenticating mobile application requests is similar to authenticating third-party API requests; however, there are small differences in how you will issue the API tokens.
+You may also use Sanctum tokens to authenticate your mobile application's requests to your API. The process for authenticating mobile application requests is similar to authenticating third-party API requests; however, there are small differences in how you will issue the API tokens.
### Issuing API Tokens
-To get started, create a route that accepts the user's email / username, password, and device name, then exchanges those credentials for a new Sanctum token. The endpoint will return the plain-text Sanctum token which may then be stored on the mobile device and used to make additional API requests:
+To get started, create a route that accepts the user's email / username, password, and device name, then exchanges those credentials for a new Sanctum token. The "device name" given to this endpoint is for informational purposes and may be any value you wish. In general, the device name value should be a name the user would recognize, such as "Nuno's iPhone 12".
+
+Typically, you will make a request to the token endpoint from your mobile application's "login" screen. The endpoint will return the plain-text API token which may then be stored on the mobile device and used to make additional API requests:
use App\Models\User;
use Illuminate\Http\Request;
@@ -312,14 +334,14 @@ To get started, create a route that accepts the user's email / username, passwor
return $user->createToken($request->device_name)->plainTextToken;
});
-When the mobile device uses the token to make an API request to your application, it should pass the token in the `Authorization` header as a `Bearer` token.
+When the mobile application uses the token to make an API request to your application, it should pass the token in the `Authorization` header as a `Bearer` token.
> {tip} When issuing tokens for a mobile application, you are also free to specify [token abilities](#token-abilities)
### Protecting Routes
-As previously documented, you may protect routes so that all incoming requests must be authenticated by attaching the `sanctum` authentication guard to the routes. Typically, you will attach this guard to the routes defined within your `routes/api.php` file:
+As previously documented, you may protect routes so that all incoming requests must be authenticated by attaching the `sanctum` authentication guard to the routes:
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
@@ -328,18 +350,18 @@ As previously documented, you may protect routes so that all incoming requests m
### Revoking Tokens
-To allow users to revoke API tokens issued to mobile devices, you may list them by name, along with a "Revoke" button, within an "account settings" portion of your web application's UI. When the user clicks the "Revoke" button, you can delete the token from the database. Remember, you can access a user's API tokens via the `tokens` relationship provided by the `HasApiTokens` trait:
+To allow users to revoke API tokens issued to mobile devices, you may list them by name, along with a "Revoke" button, within an "account settings" portion of your web application's UI. When the user clicks the "Revoke" button, you can delete the token from the database. Remember, you can access a user's API tokens via the `tokens` relationship provided by the `Laravel\Sanctum\HasApiTokens` trait:
// Revoke all tokens...
$user->tokens()->delete();
// Revoke a specific token...
- $user->tokens()->where('id', $id)->delete();
+ $user->tokens()->where('id', $tokenId)->delete();
## Testing
-While testing, the `Sanctum::actingAs` method may be used to authenticate a user and specify which abilities are granted to their token:
+While testing, the `Sanctum::actingAs` method may be used to authenticate a user and specify which abilities should be granted to their token:
use App\Models\User;
use Laravel\Sanctum\Sanctum;
diff --git a/scheduling.md b/scheduling.md
index 59ae9c6e98c..e0f0b540902 100644
--- a/scheduling.md
+++ b/scheduling.md
@@ -11,36 +11,22 @@
- [Running Tasks On One Server](#running-tasks-on-one-server)
- [Background Tasks](#background-tasks)
- [Maintenance Mode](#maintenance-mode)
+- [Running The Scheduler](#running-the-scheduler)
+ - [Running The Scheduler Locally](#running-the-scheduler-locally)
- [Task Output](#task-output)
- [Task Hooks](#task-hooks)
## Introduction
-In the past, you may have generated a Cron entry for each task you needed to schedule on your server. However, this can quickly become a pain, because your task schedule is no longer in source control and you must SSH into your server to add additional Cron entries.
+In the past, you may have written a cron configuration entry for each task you needed to schedule on your server. However, this can quickly become a pain because your task schedule is no longer in source control and you must SSH into your server to view your existing cron entries or add additional entries.
-Laravel's command scheduler allows you to fluently and expressively define your command schedule within Laravel itself. When using the scheduler, only a single Cron entry is needed on your server. Your task schedule is defined in the `app/Console/Kernel.php` file's `schedule` method. To help you get started, a simple example is defined within the method.
-
-
-### Starting The Scheduler
-
-When using the scheduler, you only need to add the following Cron entry to your server. If you do not know how to add Cron entries to your server, consider using a service such as [Laravel Forge](https://forge.laravel.com) which can manage the Cron entries for you:
-
- * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
-
-This Cron will call the Laravel command scheduler every minute. When the `schedule:run` command is executed, Laravel will evaluate your scheduled tasks and run the tasks that are due.
-
-
-### Starting The Scheduler Locally
-
-Typically, you would not add a scheduler Cron entry to your local development machine. Instead you may use the `schedule:work` Artisan command. This command will run in the foreground and invoke the scheduler every minute until you exit the command:
-
- php artisan schedule:work
+Laravel's command scheduler offers a fresh approach to managing scheduled tasks on your server. The scheduler allows you to fluently and expressively define your command schedule within your Laravel application itself. When using the scheduler, only a single cron entry is needed on your server. Your task schedule is defined in the `app/Console/Kernel.php` file's `schedule` method. To help you get started, a simple example is defined within the method.
## Defining Schedules
-You may define all of your scheduled tasks in the `schedule` method of the `App\Console\Kernel` class. 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:
+You may define all of your scheduled tasks in the `schedule` method of your application's `App\Console\Kernel` class. To get started, let's take a look at an example. 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:
call(new DeleteRecentUsers)->daily();
### Scheduling Artisan Commands
-In addition to scheduling Closure calls, you may also schedule [Artisan commands](/docs/{{version}}/artisan) and operating system commands. For example, you may use the `command` method to schedule an Artisan command using either the command's name or class:
+In addition to scheduling closures, you may also schedule [Artisan commands](/docs/{{version}}/artisan) and system commands. For example, you may use the `command` method to schedule an Artisan command using either the command's name or class.
+
+When scheduling Artisan commands using the command's class name, you may pass an array of additional command-line arguments that should be provided to the command when it is invoked:
+
+ use App\Console\Commands\SendEmailCommand;
$schedule->command('emails:send Taylor --force')->daily();
@@ -91,12 +81,18 @@ In addition to scheduling Closure calls, you may also schedule [Artisan commands
### Scheduling Queued Jobs
-The `job` method may be used to schedule a [queued job](/docs/{{version}}/queues). This method provides a convenient way to schedule jobs without using the `call` method to manually create Closures to queue the job:
+The `job` method may be used to schedule a [queued job](/docs/{{version}}/queues). This method provides a convenient way to schedule queued jobs without using the `call` method to define closures to queue the job:
+
+ use App\Jobs\Heartbeat;
$schedule->job(new Heartbeat)->everyFiveMinutes();
- // Dispatch the job to the "heartbeats" queue...
- $schedule->job(new Heartbeat, 'heartbeats')->everyFiveMinutes();
+Optional second and third arguments may be provided to the `job` method which specify the queue name and queue connection that should be used to queue the job:
+
+ use App\Jobs\Heartbeat;
+
+ // Dispatch the job to the "heartbeats" queue on the "sqs" connection...
+ $schedule->job(new Heartbeat, 'heartbeats', 'sqs')->everyFiveMinutes();
### Scheduling Shell Commands
@@ -108,11 +104,11 @@ The `exec` method may be used to issue a command to the operating system:
### Schedule Frequency Options
-There are a variety of schedules you may assign to your task:
+We've already seen a few examples of how you may configure a task to run at specified intervals. However, there are many more task schedule frequencies that you may assign to a task:
Method | Description
------------- | -------------
-`->cron('* * * * *');` | Run the task on a custom Cron schedule
+`->cron('* * * * *');` | Run the task on a custom cron schedule
`->everyMinute();` | Run the task every minute
`->everyTwoMinutes();` | Run the task every two minutes
`->everyThreeMinutes();` | Run the task every three minutes
@@ -139,9 +135,9 @@ Method | Description
`->quarterly();` | Run the task on the first day of every quarter at 00:00
`->yearly();` | Run the task on the first day of every year at 00:00
`->yearlyOn(6, 1, '17:00');` | Run the task every year on June 1st at 17:00
-`->timezone('America/New_York');` | Set the timezone
+`->timezone('America/New_York');` | Set the timezone for the task
-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:
+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, you may schedule a command to run weekly on Monday:
// Run once per week on Monday at 1 PM...
$schedule->call(function () {
@@ -155,7 +151,7 @@ These methods may be combined with additional constraints to create even more fi
->timezone('America/Chicago')
->between('8:00', '17:00');
-Below is a list of the additional schedule constraints:
+A list of additional schedule constraints may be found below:
Method | Description
------------- | -------------
@@ -179,29 +175,37 @@ Method | Description
The `days` method may be used to limit the execution of a task to specific days of the week. For example, you may schedule a command to run hourly on Sundays and Wednesdays:
- $schedule->command('reminders:send')
+ $schedule->command('emails:send')
->hourly()
->days([0, 3]);
+Alternatively, you may use the constants available on the `Illuminate\Console\Scheduling\Schedule` class when defining the days on which a task should run:
+
+ use Illuminate\Console\Scheduling\Schedule;
+
+ $schedule->command('emails:send')
+ ->hourly()
+ ->days([Schedule::SUNDAY, Schedule::WEDNESDAY]);
+
#### Between Time Constraints
The `between` method may be used to limit the execution of a task based on the time of day:
- $schedule->command('reminders:send')
+ $schedule->command('emails:send')
->hourly()
->between('7:00', '22:00');
Similarly, the `unlessBetween` method can be used to exclude the execution of a task for a period of time:
- $schedule->command('reminders:send')
+ $schedule->command('emails:send')
->hourly()
->unlessBetween('23:00', '4:00');
#### Truth Test Constraints
-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` returns `true`, the task will execute as long as no other constraining conditions prevent the task from running:
+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 returns `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;
@@ -218,7 +222,7 @@ When using chained `when` methods, the scheduled command will only execute if al
#### Environment Constraints
-The `environments` method may be used to execute tasks only on the given environments:
+The `environments` method may be used to execute tasks only on the given environments (as defined by the `APP_ENV` [environment variable](/docs/{{version}}/configuration#environment-configuration)):
$schedule->command('emails:send')
->daily()
@@ -231,9 +235,9 @@ Using the `timezone` method, you may specify that a scheduled task's time should
$schedule->command('report:generate')
->timezone('America/New_York')
- ->at('02:00')
+ ->at('2:00')
-If you are assigning the same timezone to all of your scheduled tasks, you may wish to define a `scheduleTimezone` method in your `app/Console/Kernel.php` file. This method should return the default timezone that should be assigned to all scheduled tasks:
+If you are repeatedly assigning the same timezone to all of your scheduled tasks, you may wish to define a `scheduleTimezone` method in your `App\Console\Kernel` class. This method should return the default timezone that should be assigned to all scheduled tasks:
/**
* Get the timezone that should be used by default for scheduled events.
@@ -263,9 +267,9 @@ If needed, you may specify how many minutes must pass before the "without overla
### Running Tasks On One Server
-> {note} To utilize this feature, your application must be using the `database`, `memcached`, or `redis` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server.
+> {note} To utilize this feature, your application must be using the `database`, `memcached`, `dynamodb`, or `redis` cache driver as your application's default cache driver. In addition, all servers must be communicating with the same central cache server.
-If your application is running on multiple servers, you may limit a scheduled job to only execute on a single server. For instance, assume you have a scheduled task that generates a new report every Friday night. If the task scheduler is running on three worker servers, the scheduled task will run on all three servers and generate the report three times. Not good!
+If your application's scheduler is running on multiple servers, you may limit a scheduled job to only execute on a single server. For instance, assume you have a scheduled task that generates a new report every Friday night. If the task scheduler is running on three worker servers, the scheduled task will run on all three servers and generate the report three times. Not good!
To indicate that the task should run on only one server, use the `onOneServer` method when defining the scheduled task. The first server to obtain the task will secure an atomic lock on the job to prevent other servers from running the same task at the same time:
@@ -277,7 +281,7 @@ To indicate that the task should run on only one server, use the `onOneServer` m
### Background Tasks
-By default, multiple commands scheduled at the same time will execute sequentially. If you have long-running commands, this may cause subsequent commands to start much later than anticipated. If you would like to run commands in the background so that they may all run simultaneously, you may use the `runInBackground` method:
+By default, multiple tasks scheduled at the same time will execute sequentially based on the order they are defined in your `schedule` method. If you have long-running tasks, this may cause subsequent tasks to start much later than anticipated. If you would like to run tasks in the background so that they may all run simultaneously, you may use the `runInBackground` method:
$schedule->command('analytics:report')
->daily()
@@ -288,10 +292,26 @@ By default, multiple commands scheduled at the same time will execute sequential
### Maintenance Mode
-Laravel's scheduled tasks will not run when Laravel is in [maintenance mode](/docs/{{version}}/configuration#maintenance-mode), since we don't want your tasks to interfere with any unfinished maintenance you may be performing on your server. However, if you would like to force a task to run even in maintenance mode, you may use the `evenInMaintenanceMode` method:
+Your application's scheduled tasks will not run when the application is in [maintenance mode](/docs/{{version}}/configuration#maintenance-mode), since we don't want your tasks to interfere with any unfinished maintenance you may be performing on your server. However, if you would like to force a task to run even in maintenance mode, you may call the `evenInMaintenanceMode` method when defining the task:
$schedule->command('emails:send')->evenInMaintenanceMode();
+
+## Running The Scheduler
+
+Now that we have learned how to define scheduled tasks, let's discuss how to actually run them on our server. The `schedule:run` Artisan command will evaluate all of your scheduled tasks and determine if they need to run based on the server's current time.
+
+So, when using Laravel's scheduler, we only need to add a single cron configuration entry to our server that runs the `schedule:run` command every minute. If you do not know how to add cron entries to your server, consider using a service such as [Laravel Forge](https://forge.laravel.com) which can manage the cron entries for you:
+
+ * * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
+
+
+## Running The Scheduler Locally
+
+Typically, you would not add a scheduler cron entry to your local development machine. Instead you may use the `schedule:work` Artisan command. This command will run in the foreground and invoke the scheduler every minute until you terminate the command:
+
+ php artisan schedule:work
+
## Task Output
@@ -307,36 +327,36 @@ If you would like to append the output to a given file, you may use the `appendO
->daily()
->appendOutputTo($filePath);
-Using the `emailOutputTo` method, you may e-mail the output to an e-mail address of your choice. Before e-mailing the output of a task, you should configure Laravel's [e-mail services](/docs/{{version}}/mail):
+Using the `emailOutputTo` method, you may email the output to an email address of your choice. Before emailing the output of a task, you should configure Laravel's [email services](/docs/{{version}}/mail):
- $schedule->command('foo')
+ $schedule->command('report:generate')
->daily()
->sendOutputTo($filePath)
- ->emailOutputTo('foo@example.com');
+ ->emailOutputTo('taylor@example.com');
-If you only want to e-mail the output if the command fails, use the `emailOutputOnFailure` method:
+If you only want to email the output if the scheduled Artisan or system command terminates with a non-zero exit code, use the `emailOutputOnFailure` method:
- $schedule->command('foo')
+ $schedule->command('report:generate')
->daily()
- ->emailOutputOnFailure('foo@example.com');
+ ->emailOutputOnFailure('taylor@example.com');
> {note} The `emailOutputTo`, `emailOutputOnFailure`, `sendOutputTo`, and `appendOutputTo` methods are exclusive to the `command` and `exec` methods.
## Task Hooks
-Using the `before` and `after` methods, you may specify code to be executed before and after the scheduled task is complete:
+Using the `before` and `after` methods, you may specify code to be executed before and after the scheduled task is executed:
$schedule->command('emails:send')
->daily()
->before(function () {
- // Task is about to start...
+ // The task is about to execute...
})
->after(function () {
- // Task is complete...
+ // The task has executed...
});
-The `onSuccess` and `onFailure` methods allow you to specify code to be executed if the scheduled task succeeds or fails:
+The `onSuccess` and `onFailure` methods allow you to specify code to be executed if the scheduled task succeeds or fails. A failure indicates that the scheduled Artisan or system command terminated with a non-zero exit code:
$schedule->command('emails:send')
->daily()
@@ -347,7 +367,7 @@ The `onSuccess` and `onFailure` methods allow you to specify code to be executed
// The task failed...
});
-If output is available from your command, you may access it in your `after`, `onSuccess` or `onFailure` hooks by type-hinting an `Illuminate\Support\Stringable` instance as `$output` on your hook's Closure definition:
+If output is available from your command, you may access it in your `after`, `onSuccess` or `onFailure` hooks by type-hinting an `Illuminate\Support\Stringable` instance as the `$output` argument of your hook's closure definition:
use Illuminate\Support\Stringable;
@@ -363,27 +383,27 @@ If output is available from your command, you may access it in your `after`, `on
#### 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, such as [Laravel Envoyer](https://envoyer.io), that your scheduled task is commencing or has finished execution:
+Using the `pingBefore` and `thenPing` methods, the scheduler can automatically ping a given URL before or after a task is executed. This method is useful for notifying an external service, such as [Envoyer](https://envoyer.io), that your scheduled task is beginning or has finished execution:
$schedule->command('emails:send')
->daily()
->pingBefore($url)
->thenPing($url);
-The `pingBeforeIf` and `thenPingIf` methods may be used to ping a given URL only if the given condition is `true`:
+The `pingBeforeIf` and `thenPingIf` methods may be used to ping a given URL only if a given condition is `true`:
$schedule->command('emails:send')
->daily()
->pingBeforeIf($condition, $url)
->thenPingIf($condition, $url);
-The `pingOnSuccess` and `pingOnFailure` methods may be used to ping a given URL only if the task succeeds or fails:
+The `pingOnSuccess` and `pingOnFailure` methods may be used to ping a given URL only if the task succeeds or fails. A failure indicates that the scheduled Artisan or system command terminated with a non-zero exit code:
$schedule->command('emails:send')
->daily()
->pingOnSuccess($successUrl)
->pingOnFailure($failureUrl);
-All of the ping methods require the Guzzle HTTP library. You can add Guzzle to your project using the Composer package manager:
+All of the ping methods require the Guzzle HTTP library. Guzzle is typically installed in all new Laravel projects by default, but, you may manually install Guzzle into your project using the Composer package manager if it has been accidentally removed:
composer require guzzlehttp/guzzle
diff --git a/scout.md b/scout.md
index dff7bd17939..6bba3adced5 100644
--- a/scout.md
+++ b/scout.md
@@ -2,8 +2,8 @@
- [Introduction](#introduction)
- [Installation](#installation)
- - [Queueing](#queueing)
- [Driver Prerequisites](#driver-prerequisites)
+ - [Queueing](#queueing)
- [Configuration](#configuration)
- [Configuring Model Indexes](#configuring-model-indexes)
- [Configuring Searchable Data](#configuring-searchable-data)
@@ -38,11 +38,11 @@ First, install Scout via the Composer package manager:
composer require laravel/scout
-After installing Scout, you should publish the Scout configuration using the `vendor:publish` Artisan command. This command will publish the `scout.php` configuration file to your `config` directory:
+After installing Scout, you should publish the Scout configuration file using the `vendor:publish` Artisan command. This command will publish the `scout.php` configuration file to your application's `config` directory:
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
-Finally, add the `Laravel\Scout\Searchable` trait to the model you would like to make searchable. This trait will register a model observer to keep the model in sync with your search driver:
+Finally, add the `Laravel\Scout\Searchable` trait to the model you would like to make searchable. This trait will register a model observer that will automatically keep the model in sync with your search driver:
-### Queueing
-
-While not strictly required to use Scout, you should strongly consider configuring a [queue driver](/docs/{{version}}/queues) before using the library. Running a queue worker will allow Scout to queue all operations that sync your model information to your search indexes, providing much better response times for your application's web interface.
-
-Once you have configured a queue driver, set the value of the `queue` option in your `config/scout.php` configuration file to `true`:
-
- 'queue' => true,
-
### Driver Prerequisites
@@ -75,6 +66,15 @@ When using the Algolia driver, you should configure your Algolia `id` and `secre
composer require algolia/algoliasearch-client-php
+
+### Queueing
+
+While not strictly required to use Scout, you should strongly consider configuring a [queue driver](/docs/{{version}}/queues) before using the library. Running a queue worker will allow Scout to queue all operations that sync your model information to your search indexes, providing much better response times for your application's web interface.
+
+Once you have configured a queue driver, set the value of the `queue` option in your `config/scout.php` configuration file to `true`:
+
+ 'queue' => true,
+
## Configuration
@@ -95,7 +95,7 @@ Each Eloquent model is synced with a given search "index", which contains all of
use Searchable;
/**
- * Get the index name for the model.
+ * Get the name of the index associated with the model.
*
* @return string
*/
@@ -130,7 +130,7 @@ By default, the entire `toArray` form of a given model will be persisted to its
{
$array = $this->toArray();
- // Customize array...
+ // Customize the data array...
return $array;
}
@@ -139,7 +139,7 @@ By default, the entire `toArray` form of a given model will be persisted to its
### Configuring The Model ID
-By default, Scout will use the primary key of the model as the unique ID stored in the search index. If you need to customize this behavior, you may override the `getScoutKey` and the `getScoutKeyName` methods on the model:
+By default, Scout will use the primary key of the model as model's unique ID / key that is stored in the search index. If you need to customize this behavior, you may override the `getScoutKey` and the `getScoutKeyName` methods on the model:
### Identifying Users
-Scout also allows you to auto identify users when using Algolia. Associating the authenticated user with search operations may be helpful when viewing your search analytics within Algolia's dashboard. You can enable user identification by setting `SCOUT_IDENTIFY` to `true` in your `.env` file:
+Scout also allows you to auto identify users when using [Algolia](https://algolia.com). Associating the authenticated user with search operations may be helpful when viewing your search analytics within Algolia's dashboard. You can enable user identification by defining a `SCOUT_IDENTIFY` environment variable as `true` in your application's `.env` file:
SCOUT_IDENTIFY=true
@@ -188,7 +188,7 @@ Enabling this feature this will also pass the request's IP address and your auth
### Batch Import
-If you are installing Scout into an existing project, you may already have database records you need to import into your search driver. Scout provides an `import` Artisan command that you may use to import all of your existing records into your search indexes:
+If you are installing Scout into an existing project, you may already have database records you need to import into your indexes. Scout provides a `scout:import` Artisan command that you may use to import all of your existing records into your search indexes:
php artisan scout:import "App\Models\Post"
@@ -215,78 +215,91 @@ If you would like to modify the query that is used to retrieve all of your model
### Adding Records
-Once you have added the `Laravel\Scout\Searchable` trait to a model, all you need to do is `save` a model instance and it will automatically be added to your search index. If you have configured Scout to [use queues](#queueing) this operation will be performed in the background by your queue worker:
+Once you have added the `Laravel\Scout\Searchable` trait to a model, all you need to do is `save` or `create` a model instance and it will automatically be added to your search index. If you have configured Scout to [use queues](#queueing) this operation will be performed in the background by your queue worker:
+
+ use App\Models\Order;
- $order = new App\Models\Order;
+ $order = new Order;
// ...
$order->save();
-
-#### Adding Via Query
+
+#### Adding Records Via Query
+
+If you would like to add a collection of models to your search index via an Eloquent query, you may chain the `searchable` method onto the Eloquent query. The `searchable` method will [chunk the results](/docs/{{version}}/eloquent#chunking-results) of the query and add the records to your search index. Again, if you have configured Scout to use queues, all of the chunks will be imported in the background by your queue workers:
+
+ use App\Models\Order;
-If you would like to add a collection of models to your search index via an Eloquent query, you may chain the `searchable` method onto an Eloquent query. The `searchable` method will [chunk the results](/docs/{{version}}/eloquent#chunking-results) of the query and add the records to your search index. Again, if you have configured Scout to use queues, all of the chunks will be added in the background by your queue workers:
+ Order::where('price', '>', 100)->searchable();
- // Adding via Eloquent query...
- App\Models\Order::where('price', '>', 100)->searchable();
+You may also call the `searchable` method on an Eloquent relationship instance:
- // You may also add records via relationships...
$user->orders()->searchable();
- // You may also add records via collections...
+Or, if you already have a collection of Eloquent models in memory, you may call the `searchable` method on the collection instance to add the model instances to their corresponding index:
+
$orders->searchable();
-The `searchable` method can be considered an "upsert" operation. In other words, if the model record is already in your index, it will be updated. If it does not exist in the search index, it will be added to the index.
+> {tip} The `searchable` method can be considered an "upsert" operation. In other words, if the model record is already in your index, it will be updated. If it does not exist in the search index, it will be added to the index.
### Updating Records
To update a searchable model, you only need to update the model instance's properties and `save` the model to your database. Scout will automatically persist the changes to your search index:
- $order = App\Models\Order::find(1);
+ use App\Models\Order;
+
+ $order = Order::find(1);
// Update the order...
$order->save();
-You may also use the `searchable` method on an Eloquent query to update a collection of models. If the models do not exist in your search index, they will be created:
+You may also invoke the `searchable` method on an Eloquent query instance to update a collection of models. If the models do not exist in your search index, they will be created:
- // Updating via Eloquent query...
- App\Models\Order::where('price', '>', 100)->searchable();
+ Order::where('price', '>', 100)->searchable();
+
+If you would like to update the search index records for all of the models in a relationship, you may invoke the `searchable` on the relationship instance:
- // You may also update via relationships...
$user->orders()->searchable();
- // You may also update via collections...
+Or, if you already have a collection of Eloquent models in memory, you may call the `searchable` method on the collection instance to update the model instances in their corresponding index:
+
$orders->searchable();
### Removing Records
-To remove a record from your index, `delete` the model from the database. This form of removal is even compatible with [soft deleted](/docs/{{version}}/eloquent#soft-deleting) models:
+To remove a record from your index you may simply `delete` the model from the database. This may be done even if you are using [soft deleted](/docs/{{version}}/eloquent#soft-deleting) models:
- $order = App\Models\Order::find(1);
+ use App\Models\Order;
+
+ $order = Order::find(1);
$order->delete();
-If you do not want to retrieve the model before deleting the record, you may use the `unsearchable` method on an Eloquent query instance or collection:
+If you do not want to retrieve the model before deleting the record, you may use the `unsearchable` method on an Eloquent query instance:
+
+ Order::where('price', '>', 100)->unsearchable();
- // Removing via Eloquent query...
- App\Models\Order::where('price', '>', 100)->unsearchable();
+If you would like to remove the search index records for all of the models in a relationship, you may invoke the `unsearchable` on the relationship instance:
- // You may also remove via relationships...
$user->orders()->unsearchable();
- // You may also remove via collections...
+Or, if you already have a collection of Eloquent models in memory, you may call the `unsearchable` method on the collection instance to remove the model instances from their corresponding index:
+
$orders->unsearchable();
### Pausing Indexing
-Sometimes you may need to perform a batch of Eloquent operations on a model without syncing the model data to your search index. You may do this using the `withoutSyncingToSearch` method. This method accepts a single callback which will be immediately executed. Any model operations that occur within the callback will not be synced to the model's index:
+Sometimes you may need to perform a batch of Eloquent operations on a model without syncing the model data to your search index. You may do this using the `withoutSyncingToSearch` method. This method accepts a single closure which will be immediately executed. Any model operations that occur within the closure will not be synced to the model's index:
+
+ use App\Models\Order;
- App\Models\Order::withoutSyncingToSearch(function () {
+ Order::withoutSyncingToSearch(function () {
// Perform model actions...
});
@@ -295,77 +308,91 @@ Sometimes you may need to perform a batch of Eloquent operations on a model with
Sometimes you may need to only make a model searchable under certain conditions. For example, imagine you have `App\Models\Post` model that may be in one of two states: "draft" and "published". You may only want to allow "published" posts to be searchable. To accomplish this, you may define a `shouldBeSearchable` method on your model:
+ /**
+ * Determine if the model should be searchable.
+ *
+ * @return bool
+ */
public function shouldBeSearchable()
{
return $this->isPublished();
}
-The `shouldBeSearchable` method is only applied when manipulating models through the `save` method, queries, or relationships. Directly making models or collections searchable using the `searchable` method will override the result of the `shouldBeSearchable` method:
-
- // Will respect "shouldBeSearchable"...
- App\Models\Order::where('price', '>', 100)->searchable();
-
- $user->orders()->searchable();
-
- $order->save();
-
- // Will override "shouldBeSearchable"...
- $orders->searchable();
-
- $order->searchable();
+The `shouldBeSearchable` method is only applied when manipulating models through the `save` and `create` methods, queries, or relationships. Directly making models or collections searchable using the `searchable` method will override the result of the `shouldBeSearchable` method.
## Searching
You may begin searching a model using the `search` method. The search method accepts a single string that will be used to search your models. You should then chain the `get` method onto the search query to retrieve the Eloquent models that match the given search query:
- $orders = App\Models\Order::search('Star Trek')->get();
+ use App\Models\Order;
+
+ $orders = Order::search('Star Trek')->get();
Since Scout searches return a collection of Eloquent models, you may even return the results directly from a route or controller and they will automatically be converted to JSON:
+ use App\Models\Order;
use Illuminate\Http\Request;
Route::get('/search', function (Request $request) {
- return App\Models\Order::search($request->search)->get();
+ return Order::search($request->search)->get();
});
-If you would like to get the raw results before they are converted to Eloquent models, you should use the `raw` method:
+If you would like to get the raw search results before they are converted to Eloquent models, you may use the `raw` method:
+
+ $orders = Order::search('Star Trek')->raw();
- $orders = App\Models\Order::search('Star Trek')->raw();
+
+#### Custom Indexes
Search queries will typically be performed on the index specified by the model's [`searchableAs`](#configuring-model-indexes) method. However, you may use the `within` method to specify a custom index that should be searched instead:
- $orders = App\Models\Order::search('Star Trek')
+ $orders = Order::search('Star Trek')
->within('tv_shows_popularity_desc')
->get();
### Where Clauses
-Scout allows you to add simple "where" clauses to your search queries. Currently, these clauses only support basic numeric equality checks, and are primarily useful for scoping search queries by a tenant ID. Since a search index is not a relational database, more advanced "where" clauses are not currently supported:
+Scout allows you to add simple "where" clauses to your search queries. Currently, these clauses only support basic numeric equality checks and are primarily useful for scoping search queries by an owner ID. Since a search index is not a relational database, more advanced "where" clauses are not currently supported:
- $orders = App\Models\Order::search('Star Trek')->where('user_id', 1)->get();
+ use App\Models\Order;
+
+ $orders = Order::search('Star Trek')->where('user_id', 1)->get();
### Pagination
-In addition to retrieving a collection of models, you may paginate your search results using the `paginate` method. This method will return a `Paginator` instance just as if you had [paginated a traditional Eloquent query](/docs/{{version}}/pagination):
+In addition to retrieving a collection of models, you may paginate your search results using the `paginate` method. This method will return an `Illuminate\Pagination\LengthAwarePaginator` instance just as if you had [paginated a traditional Eloquent query](/docs/{{version}}/pagination):
+
+ use App\Models\Order;
- $orders = App\Models\Order::search('Star Trek')->paginate();
+ $orders = Order::search('Star Trek')->paginate();
You may specify how many models to retrieve per page by passing the amount as the first argument to the `paginate` method:
- $orders = App\Models\Order::search('Star Trek')->paginate(15);
+ $orders = Order::search('Star Trek')->paginate(15);
Once you have retrieved the results, you may display the results and render the page links using [Blade](/docs/{{version}}/blade) just as if you had paginated a traditional Eloquent query:
-
+
+{{ $orders->links() }}
+```
+
+Of course, if you would like to retrieve the pagination results as JSON, you may return the paginator instance directly from a route or controller:
- {{ $orders->links() }}
+ use App\Models\Order;
+ use Illuminate\Http\Request;
+
+ Route::get('/orders', function (Request $request) {
+ return Order::search($request->input('query'))->paginate(15);
+ });
### Soft Deleting
@@ -376,29 +403,35 @@ If your indexed models are [soft deleting](/docs/{{version}}/eloquent#soft-delet
When this configuration option is `true`, Scout will not remove soft deleted models from the search index. Instead, it will set a hidden `__soft_deleted` attribute on the indexed record. Then, you may use the `withTrashed` or `onlyTrashed` methods to retrieve the soft deleted records when searching:
+ use App\Models\Order;
+
// Include trashed records when retrieving results...
- $orders = App\Models\Order::search('Star Trek')->withTrashed()->get();
+ $orders = Order::search('Star Trek')->withTrashed()->get();
// Only include trashed records when retrieving results...
- $orders = App\Models\Order::search('Star Trek')->onlyTrashed()->get();
+ $orders = Order::search('Star Trek')->onlyTrashed()->get();
> {tip} When a soft deleted model is permanently deleted using `forceDelete`, Scout will remove it from the search index automatically.
### Customizing Engine Searches
-If you need to customize the search behavior of an engine you may pass a callback as the second argument to the `search` method. For example, you could use this callback to add geo-location data to your search options before the search query is passed to Algolia:
+If you need to perform advanced customization of the search behavior of an engine you may pass a closure as the second argument to the `search` method. For example, you could use this callback to add geo-location data to your search options before the search query is passed to Algolia:
use Algolia\AlgoliaSearch\SearchIndex;
+ use App\Models\Order;
- App\Models\Order::search('Star Trek', function (SearchIndex $algolia, string $query, array $options) {
- $options['body']['query']['bool']['filter']['geo_distance'] = [
- 'distance' => '1000km',
- 'location' => ['lat' => 36, 'lon' => 111],
- ];
+ Order::search(
+ 'Star Trek',
+ function (SearchIndex $algolia, string $query, array $options) {
+ $options['body']['query']['bool']['filter']['geo_distance'] = [
+ 'distance' => '1000km',
+ 'location' => ['lat' => 36, 'lon' => 111],
+ ];
- return $algolia->search($query, $options);
- })->get();
+ return $algolia->search($query, $options);
+ }
+ )->get();
## Custom Engines
@@ -424,8 +457,9 @@ You may find it helpful to review the implementations of these methods on the `L
#### Registering The Engine
-Once you have written your custom engine, you may register it with Scout using the `extend` method of the Scout engine manager. You should call the `extend` method from the `boot` method of your `AppServiceProvider` or any other service provider used by your application. For example, if you have written a `MySqlSearchEngine`, you may register it like so:
+Once you have written your custom engine, you may register it with Scout using the `extend` method of the Scout engine manager. Scout's engine manager may be resolved from the Laravel service container. You should call the `extend` method from the `boot` method of your `App\Providers\AppServiceProvider` class or any other service provider used by your application:
+ use App\ScoutExtensions\MySqlSearchEngine
use Laravel\Scout\EngineManager;
/**
@@ -440,40 +474,35 @@ Once you have written your custom engine, you may register it with Scout using t
});
}
-Once your engine has been registered, you may specify it as your default Scout `driver` in your `config/scout.php` configuration file:
+Once your engine has been registered, you may specify it as your default Scout `driver` in your application's `config/scout.php` configuration file:
'driver' => 'mysql',
## Builder Macros
-If you would like to define a custom builder method, you may use the `macro` method on the `Laravel\Scout\Builder` class. Typically, "macros" should be defined within a [service provider's](/docs/{{version}}/providers) `boot` method:
-
- engine->getTotalCount(
- $this->engine()->search($this)
- );
- });
- }
+ Builder::macro('count', function () {
+ return $this->engine->getTotalCount(
+ $this->engine()->search($this)
+ );
+ });
}
-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 from a `Laravel\Scout\Builder` implementation:
+The `macro` function accepts a macro name as its first argument and a closure as its second argument. The macro's closure will be executed when calling the macro name from a `Laravel\Scout\Builder` implementation:
+
+ use App\Models\Order;
- App\Models\Order::search('Star Trek')->count();
+ Order::search('Star Trek')->count();
diff --git a/seeding.md b/seeding.md
index 8587981a428..169b90af480 100644
--- a/seeding.md
+++ b/seeding.md
@@ -9,7 +9,9 @@
## Introduction
-Laravel includes a simple method of seeding your database with test data using seed classes. All seed classes are stored in the `database/seeders` directory. Seed classes may have any name you wish, but probably should follow some sensible convention, such as `UserSeeder`, etc. By default, a `DatabaseSeeder` class is defined for you. From this class, you may use the `call` method to run other seed classes, allowing you to control the seeding order.
+Laravel includes the ability to seed your database with test data using seed classes. All seed classes are stored in the `database/seeders` directory. By default, a `DatabaseSeeder` class is defined for you. From this class, you may use the `call` method to run other seed classes, allowing you to control the seeding order.
+
+> {tip} [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled during database seeding.
## Writing Seeders
@@ -20,8 +22,6 @@ To generate a seeder, execute the `make:seeder` [Artisan command](/docs/{{versio
A seeder class only contains one method by default: `run`. This method is called when the `db:seed` [Artisan command](/docs/{{version}}/artisan) is executed. Within the `run` method, you may insert data into your database however you wish. You may use the [query builder](/docs/{{version}}/queries) to manually insert data or you may use [Eloquent model factories](/docs/{{version}}/database-testing#writing-factories).
-> {tip} [Mass assignment protection](/docs/{{version}}/eloquent#mass-assignment) is automatically disabled during database seeding.
-
As an example, let's modify the default `DatabaseSeeder` class and add a database insert statement to the `run` method:
### Calling Additional Seeders
-Within the `DatabaseSeeder` class, you may use the `call` method to execute additional seed classes. Using the `call` method allows you to break up your database seeding into multiple files so that no single seeder class becomes overwhelmingly large. Pass the name of the seeder class you wish to run:
+Within the `DatabaseSeeder` class, you may use the `call` method to execute additional seed classes. Using the `call` method allows you to break up your database seeding into multiple files so that no single seeder class becomes too large. The `call` method accepts an array of seeder classes that should be executed:
/**
* Run the database seeders.
@@ -96,19 +96,19 @@ Within the `DatabaseSeeder` class, you may use the `call` method to execute addi
## Running Seeders
-You may use the `db:seed` Artisan command to seed your database. By default, the `db:seed` command runs the `DatabaseSeeder` class, which may be used to call other seed classes. However, you may use the `--class` option to specify a specific seeder class to run individually:
+You may execute the `db:seed` Artisan command to seed your database. By default, the `db:seed` command runs the `Database\Seeders\DatabaseSeeder` class, which may in turn invoke other seed classes. However, you may use the `--class` option to specify a specific seeder class to run individually:
php artisan db:seed
php artisan db:seed --class=UserSeeder
-You may also seed your database using the `migrate:fresh` command, which will drop all tables and re-run all of your migrations. This command is useful for completely re-building your database:
+You may also seed your database using the `migrate:fresh` command in combination with the `--seed` option, which will drop all tables and re-run all of your migrations. This command is useful for completely re-building your database:
php artisan migrate:fresh --seed
#### Forcing Seeders To Run In Production
-Some seeding operations may cause you to alter or lose data. In order to protect you from running seeding commands against your production database, you will be prompted for confirmation before the seeders are executed. To force the seeders to run without a prompt, use the `--force` flag:
+Some seeding operations may cause you to alter or lose data. In order to protect you from running seeding commands against your production database, you will be prompted for confirmation before the seeders are executed in the `production` environment. To force the seeders to run without a prompt, use the `--force` flag:
php artisan db:seed --force
diff --git a/session.md b/session.md
index 32230c78ac1..0469ad707ba 100644
--- a/session.md
+++ b/session.md
@@ -3,7 +3,7 @@
- [Introduction](#introduction)
- [Configuration](#configuration)
- [Driver Prerequisites](#driver-prerequisites)
-- [Using The Session](#using-the-session)
+- [Interacting With The Session](#interacting-with-the-session)
- [Retrieving Data](#retrieving-data)
- [Storing Data](#storing-data)
- [Flash Data](#flash-data)
@@ -17,12 +17,14 @@
## Introduction
-Since HTTP driven applications are stateless, sessions provide a way to store information about the user across multiple requests. Laravel ships with a variety of session backends that are accessed through an expressive, unified API. Support for popular backends such as [Memcached](https://memcached.org), [Redis](https://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 multiple requests. That user information is typically placed in a persistent store / backend that can be accessed from subsequent requests.
+
+Laravel ships with a variety of session backends that are accessed through an expressive, unified API. Support for popular backends such as [Memcached](https://memcached.org), [Redis](https://redis.io), and databases is included.
### Configuration
-The session configuration file is stored at `config/session.php`. Be sure to review the options available to you in this file. By default, Laravel is configured to use the `file` session driver, which will work well for many applications.
+Your application's session configuration file is stored at `config/session.php`. Be sure to review the options available to you in this file. By default, Laravel is configured to use the `file` session driver, which will work well for many applications. If your application will be load balanced across multiple web servers, you should choose a centralized store that all servers can access, such as Redis or a database.
The session `driver` configuration option defines where session data will be stored for each request. Laravel ships with several great drivers out of the box:
@@ -34,7 +36,7 @@ The session `driver` configuration option defines where session data will be sto
- `array` - sessions are stored in a PHP array and will not be persisted.
-> {tip} The array driver is used during [testing](/docs/{{version}}/testing) and prevents the data stored in the session from being persisted.
+> {tip} The array driver is primarily used during [testing](/docs/{{version}}/testing) and prevents the data stored in the session from being persisted.
### Driver Prerequisites
@@ -42,7 +44,7 @@ The session `driver` configuration option defines where session data will be sto
#### Database
-When using the `database` session driver, you will need to create a table to contain the session items. Below is an example `Schema` declaration for the table:
+When using the `database` session driver, you will need to create a table to contain the session records. An example `Schema` declaration for the table may be found below:
Schema::create('sessions', function ($table) {
$table->string('id')->primary();
@@ -53,7 +55,7 @@ When using the `database` session driver, you will need to create a table to con
$table->integer('last_activity')->index();
});
-You may use the `session:table` Artisan command to generate this migration:
+You may use the `session:table` Artisan command to generate this migration. To learn more about database migrations, you may consult the complete [migration documentation](/docs/{{version}}/migrations):
php artisan session:table
@@ -62,17 +64,17 @@ You may use the `session:table` Artisan command to generate this migration:
#### Redis
-Before using Redis sessions with Laravel, you will need to either install the PhpRedis PHP extension via PECL or install the `predis/predis` package (~1.0) via Composer. For more information on configuring Redis, consult its [Laravel documentation page](/docs/{{version}}/redis#configuration).
+Before using Redis sessions with Laravel, you will need to either install the PhpRedis PHP extension via PECL or install the `predis/predis` package (~1.0) via Composer. For more information on configuring Redis, consult Laravel's [Redis documentation](/docs/{{version}}/redis#configuration).
> {tip} In the `session` configuration file, the `connection` option may be used to specify which Redis connection is used by the session.
-
-## Using The Session
+
+## Interacting With The Session
### Retrieving Data
-There are two primary ways of working with session data in Laravel: the global `session` helper and via a `Request` instance. First, let's look at accessing the session via a `Request` instance, which can be type-hinted on a controller method. Remember, controller method dependencies are automatically injected via the Laravel [service container](/docs/{{version}}/container):
+There are two primary ways of working with session data in Laravel: the global `session` helper and via a `Request` instance. First, let's look at accessing the session via a `Request` instance, which can be type-hinted on a route closure or controller method. Remember, controller method dependencies are automatically injected via the Laravel [service container](/docs/{{version}}/container):
session()->get('key', 'default');
@@ -111,7 +113,7 @@ When you retrieve an item from the session, you may also pass a default value as
You may also use the global `session` PHP function to retrieve and store data in the session. When the `session` helper is called with a single, string argument, it will return the value of that session key. When the helper is called with an array of key / value pairs, those values will be stored in the session:
- Route::get('home', function () {
+ Route::get('/home', function () {
// Retrieve a piece of data from the session...
$value = session('key');
@@ -140,7 +142,7 @@ To determine if an item is present in the session, you may use the `has` method.
//
}
-To determine if an item is present in the session, even if its value is `null`, you may use the `exists` method. The `exists` method returns `true` if the item is present:
+To determine if an item is present in the session, even if its value is `null`, you may use the `exists` method:
if ($request->session()->exists('users')) {
//
@@ -149,12 +151,12 @@ To determine if an item is present in the session, even if its value is `null`,
### Storing Data
-To store data in the session, you will typically use the `put` method or the `session` helper:
+To store data in the session, you will typically use the request instance's `put` method or the `session` helper:
// Via a request instance...
$request->session()->put('key', 'value');
- // Via the global helper...
+ // Via the global "session" helper...
session(['key' => 'value']);
@@ -174,11 +176,11 @@ The `pull` method will retrieve and delete an item from the session in a single
### Flash Data
-Sometimes you may wish to store items in the session only for the next request. You may do so using the `flash` method. Data stored in the session using this method will be available immediately and during the subsequent HTTP request. After the subsequent HTTP request, the flashed data will be deleted. Flash data is primarily useful for short-lived status messages:
+Sometimes you may wish to store items in the session for the next request. You may do so using the `flash` method. Data stored in the session using this method will be available immediately and during the subsequent HTTP request. After the subsequent HTTP request, the flashed data will be deleted. Flash data is primarily useful for short-lived status messages:
$request->session()->flash('status', 'Task was successful!');
-If you need to keep your flash data around for several requests, you may use the `reflash` method, which will keep all of the flash data for an additional request. If you only need to keep specific flash data, you may use the `keep` method:
+If you need to persist your flash data for several requests, you may use the `reflash` method, which will keep all of the flash data for an additional request. If you only need to keep specific flash data, you may use the `keep` method:
$request->session()->reflash();
@@ -190,10 +192,10 @@ If you need to keep your flash data around for several requests, you may use the
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:
// Forget a single key...
- $request->session()->forget('key');
+ $request->session()->forget('name');
// Forget multiple keys...
- $request->session()->forget(['key1', 'key2']);
+ $request->session()->forget(['name', 'status']);
$request->session()->flush();
@@ -202,7 +204,7 @@ The `forget` method will remove a piece of data from the session. If you would l
Regenerating the session ID is often done in order to prevent malicious users from exploiting a [session fixation](https://owasp.org/www-community/attacks/Session_fixation) attack on your application.
-Laravel automatically regenerates the session ID during authentication if you are using [Laravel Jetstream](https://jetstream.laravel.com); however, if you need to manually regenerate the session ID, you may use the `regenerate` method.
+Laravel automatically regenerates the session ID during authentication if you are using one of the Laravel [application starter kits](/docs/{{version}}/starter-kits) or [Laravel Fortify](https://github.com/laravel/fortify); however, if you need to manually regenerate the session ID, you may use the `regenerate` method.
$request->session()->regenerate();
@@ -239,7 +241,7 @@ If neither of these arguments are passed, the lock will be obtained for a maximu
#### Implementing The Driver
-Your custom session driver should implement the `SessionHandlerInterface`. This interface contains just a few simple methods we need to implement. A stubbed MongoDB implementation looks something like this:
+If none of the existing session drivers fit your application's needs, Laravel makes it possible to write your own session handler. Your custom session driver should implement PHP's built-in `SessionHandlerInterface`. This interface contains just a few simple methods. A stubbed MongoDB implementation looks like the following:
-- The `open` method would typically be used in file based session store systems. Since Laravel ships with a `file` session driver, you will almost never need to put anything in this method. You can leave it as an empty stub. It is a fact of poor interface design (which we'll discuss later) that PHP requires us to implement this method.
+- The `open` method would typically be used in file based session store systems. Since Laravel ships with a `file` session driver, you will almost never need to put anything in this method. You can simply leave this method empty.
- The `close` method, like the `open` method, can also usually be disregarded. For most drivers, it is not needed.
- The `read` method should return the string version of the session data associated with the given `$sessionId`. There is no need to do any serialization or other encoding when retrieving or storing session data in your driver, as Laravel will perform the serialization for you.
-- The `write` method should write the given `$data` string associated with the `$sessionId` to some persistent storage system, such as MongoDB, Dynamo, etc. Again, you should not perform any serialization - Laravel will have already handled that for you.
+- The `write` method should write the given `$data` string associated with the `$sessionId` to some persistent storage system, such as MongoDB or another storage system of your choice. Again, you should not perform any serialization - Laravel will have already handled that for you.
- The `destroy` method should remove the data associated with the `$sessionId` from persistent storage.
- The `gc` method should destroy all session data that is older than the given `$lifetime`, which is a UNIX timestamp. For self-expiring systems like Memcached and Redis, this method may be left empty.
@@ -271,7 +273,7 @@ Since the purpose of these methods is not readily understandable, let's quickly
#### Registering The Driver
-Once your driver has been implemented, you are ready to register it with the framework. To add additional drivers to Laravel's session backend, you may use the `extend` method on the `Session` [facade](/docs/{{version}}/facades). You should call the `extend` method from the `boot` method of a [service provider](/docs/{{version}}/providers). You may do this from the existing `AppServiceProvider` or create an entirely new provider:
+Once your driver has been implemented, you are ready to register it with Laravel. To add additional drivers to Laravel's session backend, you may use the `extend` method provided by the `Session` [facade](/docs/{{version}}/facades). You should call the `extend` method from the `boot` method of a [service provider](/docs/{{version}}/providers). You may do this from the existing `App\Providers\AppServiceProvider` or create an entirely new provider:
## Introduction
-In addition to typical, form based authentication, Laravel also provides a simple, convenient way to authenticate with OAuth providers using [Laravel Socialite](https://github.com/laravel/socialite). Socialite currently supports authentication with Facebook, Twitter, LinkedIn, Google, GitHub, GitLab and Bitbucket.
+In addition to typical, form based authentication, Laravel also provides a simple, convenient way to authenticate with OAuth providers using [Laravel Socialite](https://github.com/laravel/socialite). Socialite currently supports authentication with Facebook, Twitter, LinkedIn, Google, GitHub, GitLab, and Bitbucket.
> {tip} Adapters for other platforms are listed at the community driven [Socialite Providers](https://socialiteproviders.com/) website.
-
-## Upgrading Socialite
-
-When upgrading to a new major version of Socialite, it's important that you carefully review [the upgrade guide](https://github.com/laravel/socialite/blob/master/UPGRADE.md).
-
## Installation
-To get started with Socialite, use Composer to add the package to your project's dependencies:
+To get started with Socialite, use the Composer package manager to add the package to your project's dependencies:
composer require laravel/socialite
+
+### Upgrading Socialite
+
+When upgrading to a new major version of Socialite, it's important that you carefully review [the upgrade guide](https://github.com/laravel/socialite/blob/master/UPGRADE.md).
+
## Configuration
-Before using Socialite, you will also need to add credentials for the OAuth services your application utilizes. These credentials should be placed in your `config/services.php` configuration file, and should use the key `facebook`, `twitter`, `linkedin`, `google`, `github`, `gitlab` or `bitbucket`, depending on the providers your application requires. For example:
+Before using Socialite, you will need to add credentials for the OAuth providers your application utilizes. These credentials should be placed in your application's `config/services.php` configuration file, and should use the key `facebook`, `twitter`, `linkedin`, `google`, `github`, `gitlab`, or `bitbucket`, depending on the providers your application requires:
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
- 'redirect' => 'http://your-callback-url',
+ 'redirect' => 'http://your-app.test/callback-url',
],
> {tip} If the `redirect` option contains a relative path, it will automatically be resolved to a fully qualified URL.
-
-## Routing
-
-Next, you are ready to authenticate users! You will need two routes: one for redirecting the user to the OAuth provider, and another for receiving the callback from the provider after authentication. We will access Socialite using the `Socialite` facade:
+
+## Authentication
-
+### Routing
- namespace App\Http\Controllers\Auth;
+To authenticate users using an OAuth provider, you will need two routes: one for redirecting the user to the OAuth provider, and another for receiving the callback from the provider after authentication. The example controller below demonstrates the implementation of both routes:
- use App\Http\Controllers\Controller;
use Laravel\Socialite\Facades\Socialite;
- class LoginController extends Controller
- {
- /**
- * Redirect the user to the GitHub authentication page.
- *
- * @return \Illuminate\Http\Response
- */
- public function redirectToProvider()
- {
- return Socialite::driver('github')->redirect();
- }
-
- /**
- * Obtain the user information from GitHub.
- *
- * @return \Illuminate\Http\Response
- */
- public function handleProviderCallback()
- {
- $user = Socialite::driver('github')->user();
-
- // $user->token;
- }
- }
+ Route::get('/auth/redirect', function () {
+ return Socialite::driver('github')->redirect();
+ });
-The `redirect` method takes care of sending the user to the OAuth provider, while the `user` method will read the incoming request and retrieve the user's information from the provider.
+ Route::get('/auth/callback', function () {
+ $user = Socialite::driver('github')->user();
-You will need to define routes to your controller methods:
+ // $user->token
+ });
- use App\Http\Controllers\Auth\LoginController;
-
- Route::get('login/github', [LoginController::class, 'redirectToProvider']);
- Route::get('login/github/callback', [LoginController::class, 'handleProviderCallback']);
+The `redirect` method provided by the `Socialite` facade takes care of redirecting the user to the OAuth provider, while the `user` method will read the incoming request and retrieve the user's information from the provider after they are authenticated.
-## Optional Parameters
+### Optional Parameters
A number of OAuth providers support optional parameters in the redirect request. To include any optional parameters in the request, call the `with` method with an associative array:
+ use Laravel\Socialite\Facades\Socialite;
+
return Socialite::driver('google')
->with(['hd' => 'example.com'])
->redirect();
@@ -100,62 +78,72 @@ A number of OAuth providers support optional parameters in the redirect request.
> {note} When using the `with` method, be careful not to pass any reserved keywords such as `state` or `response_type`.
-## Access Scopes
+### Access Scopes
+
+Before redirecting the user, you may also add additional "scopes" to the authentication request using the `scopes` method. This method will merge all existing scopes with the scopes that you supply:
-Before redirecting the user, you may also add additional "scopes" on the request using the `scopes` method. This method will merge all existing scopes with the ones you supply:
+ use Laravel\Socialite\Facades\Socialite;
return Socialite::driver('github')
->scopes(['read:user', 'public_repo'])
->redirect();
-You can overwrite all existing scopes using the `setScopes` method:
+You can overwrite all existing scopes on the authentication request using the `setScopes` method:
return Socialite::driver('github')
->setScopes(['read:user', 'public_repo'])
->redirect();
-
-## Stateless Authentication
-
-The `stateless` method may be used to disable session state verification. This is useful when adding social authentication to an API:
-
- return Socialite::driver('google')->stateless()->user();
-
-> {note} Stateless authentication is not available for the Twitter driver, which uses OAuth 1.0 for authentication.
-
## Retrieving User Details
-Once you have a user instance, you can grab a few more details about the user:
+After the user is redirected back to your authentication callback route, you may retrieve the user's details using Socialite's `user` method. The user object returned by the `user` method provides a variety of properties and methods you may use to store information about the user in your own database. Different properties and methods may be available depending on whether the OAuth provider you are authenticating with supports OAuth 1.0 or OAuth 2.0:
- $user = Socialite::driver('github')->user();
+ Route::get('/auth/callback', function () {
+ $user = Socialite::driver('github')->user();
- // OAuth Two Providers
- $token = $user->token;
- $refreshToken = $user->refreshToken; // not always provided
- $expiresIn = $user->expiresIn;
+ // OAuth 2.0 providers...
+ $token = $user->token;
+ $refreshToken = $user->refreshToken;
+ $expiresIn = $user->expiresIn;
- // OAuth One Providers
- $token = $user->token;
- $tokenSecret = $user->tokenSecret;
+ // OAuth 1.0 providers...
+ $token = $user->token;
+ $tokenSecret = $user->tokenSecret;
- // All Providers
- $user->getId();
- $user->getNickname();
- $user->getName();
- $user->getEmail();
- $user->getAvatar();
+ // All providers...
+ $user->getId();
+ $user->getNickname();
+ $user->getName();
+ $user->getEmail();
+ $user->getAvatar();
+ });
#### Retrieving User Details From A Token (OAuth2)
-If you already have a valid access token for a user, you can retrieve their details using the `userFromToken` method:
+If you already have a valid access token for a user, you can retrieve their details using Socialite's `userFromToken` method:
+
+ use Laravel\Socialite\Facades\Socialite;
$user = Socialite::driver('github')->userFromToken($token);
#### Retrieving User Details From A Token And Secret (OAuth1)
-If you already have a valid pair of token / secret for a user, you can retrieve their details using the `userFromTokenAndSecret` method:
+If you already have a valid token and secret for a user, you can retrieve their details using Socialite's `userFromTokenAndSecret` method:
+
+ use Laravel\Socialite\Facades\Socialite;
$user = Socialite::driver('twitter')->userFromTokenAndSecret($token, $secret);
+
+
+#### Stateless Authentication
+
+The `stateless` method may be used to disable session state verification. This is useful when adding social authentication to an API:
+
+ use Laravel\Socialite\Facades\Socialite;
+
+ return Socialite::driver('google')->stateless()->user();
+
+> {note} Stateless authentication is not available for the Twitter driver, which uses OAuth 1.0 for authentication.
diff --git a/starter-kits.md b/starter-kits.md
new file mode 100644
index 00000000000..62fd58d0499
--- /dev/null
+++ b/starter-kits.md
@@ -0,0 +1,60 @@
+# Starter Kits
+
+- [Introduction](#introduction)
+- [Laravel Breeze](#laravel-breeze)
+ - [Installation](#laravel-breeze-installation)
+- [Laravel Jetstream](#laravel-jetstream)
+
+
+## Introduction
+
+To give you a head start building your new Laravel application, we are happy to offer authentication and application starter kits. These kits automatically scaffold your application with the routes, controllers, and views you need to register and authenticate your application's users.
+
+While you are welcome to use these starter kits, they are not required. You are free to build your own application from the ground up by simply installing a fresh copy of Laravel. Either way, we know you will build something great!
+
+
+## Laravel Breeze
+
+Laravel Breeze is a minimal, simple implementation of all of Laravel's [authentication features](/docs/{{version}}/authentication), including login, registration, password reset, email verification, and password confirmation. Laravel Breeze's view layer is made up of simple [Blade templates](/docs/{{version}}/blade) styled with [Tailwind CSS](https://tailwindcss.com). Breeze provides a wonderful starting point for beginning a fresh Laravel application.
+
+
+### Installation
+
+First, you should [create a new Laravel application](/docs/{{version}}/installation), configure your database, and run your [database migrations](/docs/{{version}}/migrations):
+
+```bash
+curl -s https://laravel.build/example-app | bash
+
+cd example-app
+
+php artisan migrate
+```
+
+Once you have created a new Laravel application, you may install Laravel Breeze using Composer:
+
+```bash
+composer require laravel/breeze --dev
+```
+
+After Composer has installed the Laravel Breeze package, you may run the `breeze:install` Artisan command. This command publishes the authentication views, routes, controllers, and other resources to your application. Laravel Breeze publishes all of its code to your application so that you have full control and visibility over its features and implementation. After Breeze is installed, you should also compile your assets so that your application's CSS file is available:
+
+```bash
+php artisan breeze:install
+
+npm install
+
+npm run dev
+```
+
+Next, you may navigate to your application's `/login` or `/register` URLs in your web browser. All of Breeze's routes are defined within the `routes/auth.php` file.
+
+> {tip} To learn more about compiling your application's CSS and JavaScript, check out the [Laravel Mix documentation](/docs/{{version}}/mix#running-mix).
+
+
+## Laravel Jetstream
+
+While Laravel Breeze provides a simple and minimal starting point for building a Laravel application, Jetstream augments that functionality with more robust features and additional frontend technology stacks. **For those brand new to Laravel, we recommend learning the ropes with Laravel Breeze before graduating to Laravel Jetstream.**
+
+Jetstream provides a beautifully designed application scaffolding for Laravel and includes login, registration, email verification, two-factor authentication, session management, API support via Laravel Sanctum, and optional team management. Jetstream is designed using [Tailwind CSS](https://tailwindcss.com) and offers your choice of [Livewire](https://laravel-livewire.com) or [Inertia.js](https://inertiajs.com) driven frontend scaffolding.
+
+Complete documentation for installing Laravel Jetstream can be found within the [official Jetstream documentation](https://jetstream.laravel.com/1.x/introduction.html).
diff --git a/structure.md b/structure.md
index eb10bf95077..b4adede0c3e 100644
--- a/structure.md
+++ b/structure.md
@@ -43,7 +43,7 @@ The `app` directory contains the core code of your application. We'll explore th
#### The Bootstrap Directory
-The `bootstrap` directory contains the `app.php` file which bootstraps the framework. This directory also houses a `cache` directory which contains framework generated files for performance optimization such as the route and services cache files.
+The `bootstrap` directory contains the `app.php` file which bootstraps the framework. This directory also houses a `cache` directory which contains framework generated files for performance optimization such as the route and services cache files. You should not typically need to modify any files within this directory.
#### The Config Directory
@@ -63,32 +63,32 @@ The `public` directory contains the `index.php` file, which is the entry point f
#### The Resources Directory
-The `resources` directory contains your views as well as your raw, un-compiled assets such as CSS or JavaScript. This directory also houses all of your language files.
+The `resources` directory contains your [views](/docs/{{version}}/views) as well as your raw, un-compiled assets such as CSS or JavaScript. This directory also houses all of your language files.
#### The Routes Directory
-The `routes` directory contains all of the route definitions for your application. By default, several route files are included with Laravel: `web.php`, `api.php`, `console.php` and `channels.php`.
+The `routes` directory contains all of the route definitions for your application. By default, several route files are included with Laravel: `web.php`, `api.php`, `console.php`, and `channels.php`.
-The `web.php` file contains routes that the `RouteServiceProvider` places in the `web` middleware group, which provides session state, CSRF protection, and cookie encryption. If your application does not offer a stateless, RESTful API, all of your routes will most likely be defined in the `web.php` file.
+The `web.php` file contains routes that the `RouteServiceProvider` places in the `web` middleware group, which provides session state, CSRF protection, and cookie encryption. If your application does not offer a stateless, RESTful API then it is likely that all of your routes will most likely be defined in the `web.php` file.
-The `api.php` file contains routes that the `RouteServiceProvider` places in the `api` middleware group, which provides rate limiting. These routes are intended to be stateless, so requests entering the application through these routes are intended to be authenticated via tokens and will not have access to session state.
+The `api.php` file contains routes that the `RouteServiceProvider` places in the `api` middleware group. These routes are intended to be stateless, so requests entering the application through these routes are intended to be authenticated [via tokens](/docs/{{version}}/sanctum) and will not have access to session state.
-The `console.php` file is where you may define all of your Closure based console commands. Each Closure is bound to a command instance allowing a simple approach to interacting with each command's IO methods. Even though this file does not define HTTP routes, it defines console based entry points (routes) into your application.
+The `console.php` file is where you may define all of your closure based console commands. Each closure is bound to a command instance allowing a simple approach to interacting with each command's IO methods. Even though this file does not define HTTP routes, it defines console based entry points (routes) into your application.
-The `channels.php` file is where you may register all of the event broadcasting channels that your application supports.
+The `channels.php` file is where you may register all of the [event broadcasting](/docs/{{version}}/broadcasting) channels that your application supports.
#### The Storage Directory
-The `storage` directory contains your compiled Blade templates, file based sessions, file caches, and other files generated by the framework. This directory is segregated into `app`, `framework`, and `logs` directories. The `app` directory may be used to store any files generated by your application. The `framework` directory is used to store framework generated files and caches. Finally, the `logs` directory contains your application's log files.
+The `storage` directory contains your logs, compiled Blade templates, file based sessions, file caches, and other files generated by the framework. This directory is segregated into `app`, `framework`, and `logs` directories. The `app` directory may be used to store any files generated by your application. The `framework` directory is used to store framework generated files and caches. Finally, the `logs` directory contains your application's log files.
-The `storage/app/public` directory may be used to store user-generated files, such as profile avatars, that should be publicly accessible. You should create a symbolic link at `public/storage` which points to this directory. You may create the link using the `php artisan storage:link` command.
+The `storage/app/public` directory may be used to store user-generated files, such as profile avatars, that should be publicly accessible. You should create a symbolic link at `public/storage` which points to this directory. You may create the link using the `php artisan storage:link` Artisan command.
#### The Tests Directory
-The `tests` directory contains your automated tests. An example [PHPUnit](https://phpunit.de/) test is provided out of the box. Each test class should be suffixed with the word `Test`. You may run your tests using the `phpunit` or `php vendor/bin/phpunit` commands.
+The `tests` directory contains your automated tests. Example [PHPUnit](https://phpunit.de/) unit tests and feature tests are provided out of the box. Each test class should be suffixed with the word `Test`. You may run your tests using the `phpunit` or `php vendor/bin/phpunit` commands. Or, if you would like a more detailed and beautiful representation of your test results, you may run your tests using the `php artisan test` Artisan command.
#### The Vendor Directory
@@ -144,22 +144,22 @@ This directory does not exist by default, but will be created for you if you exe
#### The Mail Directory
-This directory does not exist by default, but will be created for you if you execute the `make:mail` Artisan command. The `Mail` directory contains all of your classes that represent emails sent by your application. Mail objects allow you to encapsulate all of the logic of building an email in a single, simple class that may be sent using the `Mail::send` method.
+This directory does not exist by default, but will be created for you if you execute the `make:mail` Artisan command. The `Mail` directory contains all of your [classes that represent emails](/docs/{{version}}/mail) sent by your application. Mail objects allow you to encapsulate all of the logic of building an email in a single, simple class that may be sent using the `Mail::send` method.
#### The Models Directory
-The `Models` directory contains all of your Eloquent model classes. The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table.
+The `Models` directory contains all of your [Eloquent model classes](/docs/{{version}}/eloquent). The Eloquent ORM included with Laravel provides a beautiful, simple ActiveRecord implementation for working with your database. Each database table has a corresponding "Model" which is used to interact with that table. Models allow you to query for data in your tables, as well as insert new records into the table.
#### The Notifications Directory
-This directory does not exist by default, but will be created for you if you execute the `make:notification` Artisan command. The `Notifications` directory contains all of the "transactional" notifications that are sent by your application, such as simple notifications about events that happen within your application. Laravel's notification features abstracts sending notifications over a variety of drivers such as email, Slack, SMS, or stored in a database.
+This directory does not exist by default, but will be created for you if you execute the `make:notification` Artisan command. The `Notifications` directory contains all of the "transactional" [notifications](/docs/{{version}}/notifications) that are sent by your application, such as simple notifications about events that happen within your application. Laravel's notification features abstracts sending notifications over a variety of drivers such as email, Slack, SMS, or stored in a database.
#### The Policies Directory
-This directory does not exist by default, but will be created for you if you execute the `make:policy` Artisan command. The `Policies` directory contains the authorization policy classes for your application. Policies are used to determine if a user can perform a given action against a resource. For more information, check out the [authorization documentation](/docs/{{version}}/authorization).
+This directory does not exist by default, but will be created for you if you execute the `make:policy` Artisan command. The `Policies` directory contains the [authorization policy classes](/docs/{{version}}/authorization) for your application. Policies are used to determine if a user can perform a given action against a resource.
#### The Providers Directory
diff --git a/telescope.md b/telescope.md
index 966dc7423fb..b42c5404b46 100644
--- a/telescope.md
+++ b/telescope.md
@@ -2,9 +2,9 @@
- [Introduction](#introduction)
- [Installation](#installation)
+ - [Local Only Installation](#local-only-installation)
- [Configuration](#configuration)
- [Data Pruning](#data-pruning)
- - [Migration Customization](#migration-customization)
- [Dashboard Authorization](#dashboard-authorization)
- [Upgrading Telescope](#upgrading-telescope)
- [Filtering](#filtering)
@@ -34,31 +34,40 @@
## Introduction
-Laravel Telescope is an elegant debug assistant for the Laravel framework. Telescope provides insight into the requests coming into your application, exceptions, log entries, database queries, queued jobs, mail, notifications, cache operations, scheduled tasks, variable dumps and more. Telescope makes a wonderful companion to your local Laravel development environment.
+Telescope makes a wonderful companion to your local Laravel development environment. Telescope provides insight into the requests coming into your application, exceptions, log entries, database queries, queued jobs, mail, notifications, cache operations, scheduled tasks, variable dumps, and more.
## Installation
-You may use Composer to install Telescope into your Laravel project:
+You may use the Composer package manager to install Telescope into your Laravel project:
composer require laravel/telescope
-After installing Telescope, publish its assets using the `telescope:install` Artisan command. After installing Telescope, you should also run the `migrate` command:
+After installing Telescope, publish its assets using the `telescope:install` Artisan command. After installing Telescope, you should also run the `migrate` command in order to create the tables needed to store Telescope's data:
php artisan telescope:install
php artisan migrate
-
-### Installing Only In Specific Environments
+
+#### Migration Customization
+
+If you are not going to use Telescope's default migrations, you should call the `Telescope::ignoreMigrations` method in the `register` method of your application's `App\Providers\AppServiceProvider` class. You may export the default migrations using the following command: `php artisan vendor:publish --tag=telescope-migrations`
+
+
+### Local Only Installation
If you plan to only use Telescope to assist your local development, you may install Telescope using the `--dev` flag:
composer require laravel/telescope --dev
-After running `telescope:install`, you should remove the `TelescopeServiceProvider` service provider registration from your `app` configuration file. Instead, manually register the service provider in the `register` method of your `AppServiceProvider`:
+ php artisan telescope:install
+
+ php artisan migrate
+
+After running `telescope:install`, you should remove the `TelescopeServiceProvider` service provider registration from your application's `config/app.php` configuration file. Instead, manually register Telescope's service providers in the `register` method of your `App\Providers\AppServiceProvider` class. We will ensure the current environment is `local` before registering the providers:
/**
* Register any application services.
@@ -73,7 +82,7 @@ After running `telescope:install`, you should remove the `TelescopeServiceProvid
}
}
-You should also prevent the Telescope package from being [auto-discovered](/docs/{{version}}/packages#package-discovery) by adding the following to your `composer.json` file:
+Finally, you should also prevent the Telescope package from being [auto-discovered](/docs/{{version}}/packages#package-discovery) by adding the following to your `composer.json` file:
"extra": {
"laravel": {
@@ -83,15 +92,10 @@ You should also prevent the Telescope package from being [auto-discovered](/docs
}
},
-
-### Migration Customization
-
-If you are not going to use Telescope's default migrations, you should call the `Telescope::ignoreMigrations` method in the `register` method of your `AppServiceProvider`. You may export the default migrations using the `php artisan vendor:publish --tag=telescope-migrations` command.
-
### Configuration
-After publishing Telescope's assets, its primary configuration file will be located at `config/telescope.php`. This configuration file allows you to configure your watcher options and each configuration option includes a description of its purpose, so be sure to thoroughly explore this file.
+After publishing Telescope's assets, its primary configuration file will be located at `config/telescope.php`. This configuration file allows you to configure your [watcher options](#available-watchers). Each configuration option includes a description of its purpose, so be sure to thoroughly explore this file.
If desired, you may disable Telescope's data collection entirely using the `enabled` configuration option:
@@ -100,7 +104,7 @@ If desired, you may disable Telescope's data collection entirely using the `enab
### Data Pruning
-Without pruning, the `telescope_entries` table can accumulate records very quickly. To mitigate this, you should schedule the `telescope:prune` Artisan command to run daily:
+Without pruning, the `telescope_entries` table can accumulate records very quickly. To mitigate this, you should [schedule](/docs/{{version}}/scheduling) the `telescope:prune` Artisan command to run daily:
$schedule->command('telescope:prune')->daily();
@@ -111,7 +115,7 @@ By default, all entries older than 24 hours will be pruned. You may use the `hou
### Dashboard Authorization
-Telescope exposes a dashboard at `/telescope`. By default, you will only be able to access this dashboard in the `local` environment. Within your `app/Providers/TelescopeServiceProvider.php` file, there is a `gate` method. This authorization gate controls access to Telescope in **non-local** environments. You are free to modify this gate as needed to restrict access to your Telescope installation:
+The Telescope dashboard may be accessed at the `/telescope` route. By default, you will only be able to access this dashboard in the `local` environment. Within your `app/Providers/TelescopeServiceProvider.php` file, there is an [authorization gate](/docs/{{version}}/authorization#gates) definition. This authorization gate controls access to Telescope in **non-local** environments. You are free to modify this gate as needed to restrict access to your Telescope installation:
/**
* Register the Telescope gate.
@@ -156,7 +160,10 @@ To keep the assets up-to-date and avoid issues in future updates, you may add th
### Entries
-You may filter the data that is recorded by Telescope via the `filter` callback that is registered in your `TelescopeServiceProvider`. By default, this callback records all data in the `local` environment and exceptions, failed jobs, scheduled tasks, and data with monitored tags in all other environments:
+You may filter the data that is recorded by Telescope via the `filter` closure that is defined in your `App\Providers\TelescopeServiceProvider` class. By default, this closure records all data in the `local` environment and exceptions, failed jobs, scheduled tasks, and data with monitored tags in all other environments:
+
+ use Laravel\Telescope\IncomingEntry;
+ use Laravel\Telescope\Telescope;
/**
* Register any application services.
@@ -182,9 +189,10 @@ You may filter the data that is recorded by Telescope via the `filter` callback
### Batches
-While the `filter` callback filters data for individual entries, you may use the `filterBatch` method to register a callback that filters all data for a given request or console command. If the callback returns `true`, all of the entries are recorded by Telescope:
+While the `filter` closure filters data for individual entries, you may use the `filterBatch` method to register a closure that filters all data for a given request or console command. If the closure returns `true`, all of the entries are recorded by Telescope:
use Illuminate\Support\Collection;
+ use Laravel\Telescope\Telescope;
/**
* Register any application services.
@@ -212,8 +220,9 @@ While the `filter` callback filters data for individual entries, you may use the
## Tagging
-Telescope allows you to search entries by "tag". Often, tags are Eloquent model class names or authenticated user IDs which Telescope automatically adds to entries. Occasionally, you may want to attach your own custom tags to entries. To accomplish this, you may use the `Telescope::tag` method. The `tag` method accepts a callback which should return an array of tags. The tags returned by the callback will be merged with any tags Telescope would automatically attach to the entry. You should call the `tag` method within your `TelescopeServiceProvider`:
+Telescope allows you to search entries by "tag". Often, tags are Eloquent model class names or authenticated user IDs which Telescope automatically adds to entries. Occasionally, you may want to attach your own custom tags to entries. To accomplish this, you may use the `Telescope::tag` method. The `tag` method accepts a closure which should return an array of tags. The tags returned by the closure will be merged with any tags Telescope would automatically attach to the entry. Typically, you should call the `tag` method within the `register` method of your `App\Providers\TelescopeServiceProvider` class:
+ use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
/**
@@ -226,18 +235,16 @@ Telescope allows you to search entries by "tag". Often, tags are Eloquent model
$this->hideSensitiveRequestDetails();
Telescope::tag(function (IncomingEntry $entry) {
- if ($entry->type === 'request') {
- return ['status:'.$entry->content['response_status']];
- }
-
- return [];
+ return $entry->type === 'request'
+ ? ['status:'.$entry->content['response_status']]
+ : [];
});
}
## Available Watchers
-Telescope watchers gather application data when a request or console command is executed. You may customize the list of watchers that you would like to enable within your `config/telescope.php` configuration file:
+Telescope "watchers" gather application data when a request or console command is executed. You may customize the list of watchers that you would like to enable within your `config/telescope.php` configuration file:
'watchers' => [
Watchers\CacheWatcher::class => true,
@@ -258,7 +265,7 @@ Some watchers also allow you to provide additional customization options:
### Batch Watcher
-The batch watcher records information about queued batches, including the job and connection information.
+The batch watcher records information about queued [batches](/docs/{{version}}/queues#job-batching), including the job and connection information.
### Cache Watcher
@@ -268,7 +275,7 @@ The cache watcher records data when a cache key is hit, missed, updated and forg
### Command Watcher
-The command watcher records the arguments, options, exit code, and output whenever an Artisan command is executed. If you would like to exclude certain commands from being recorded by the watcher, you may specify the command in the `ignore` option in your `config/telescope.php` file:
+The command watcher records the arguments, options, exit code, and output whenever an Artisan command is executed. If you would like to exclude certain commands from being recorded by the watcher, you may specify the command in the `ignore` option within your `config/telescope.php` file:
'watchers' => [
Watchers\CommandWatcher::class => [
@@ -281,22 +288,22 @@ The command watcher records the arguments, options, exit code, and output whenev
### Dump Watcher
-The dump watcher records and displays your variable dumps in Telescope. When using Laravel, variables may be dumped using the global `dump` function. The dump watcher tab must be open in a browser for the recording to occur, otherwise the dumps will be ignored by the watcher.
+The dump watcher records and displays your variable dumps in Telescope. When using Laravel, variables may be dumped using the global `dump` function. The dump watcher tab must be open in a browser for the dump to be recorded, otherwise the dumps will be ignored by the watcher.
### Event Watcher
-The event watcher records the payload, listeners, and broadcast data for any events dispatched by your application. The Laravel framework's internal events are ignored by the Event watcher.
+The event watcher records the payload, listeners, and broadcast data for any [events](/docs/{{version}}/events) dispatched by your application. The Laravel framework's internal events are ignored by the Event watcher.
### Exception Watcher
-The exception watcher records the data and stack trace for any reportable Exceptions that are thrown by your application.
+The exception watcher records the data and stack trace for any reportable exceptions that are thrown by your application.
### Gate Watcher
-The gate watcher records the data and result of gate and policy checks by your application. If you would like to exclude certain abilities from being recorded by the watcher, you may specify those in the `ignore_abilities` option in your `config/telescope.php` file:
+The gate watcher records the data and result of [gate and policy](/docs/{{version}}/authorization) checks by your application. If you would like to exclude certain abilities from being recorded by the watcher, you may specify those in the `ignore_abilities` option in your `config/telescope.php` file:
'watchers' => [
Watchers\GateWatcher::class => [
@@ -309,22 +316,22 @@ The gate watcher records the data and result of gate and policy checks by your a
### Job Watcher
-The job watcher records the data and status of any jobs dispatched by your application.
+The job watcher records the data and status of any [jobs](/docs/{{version}}/queues) dispatched by your application.
### Log Watcher
-The log watcher records the log data for any logs written by your application.
+The log watcher records the [log data](/docs/{{version}}/logging) for any logs written by your application.
### Mail Watcher
-The mail watcher allows you to view an in-browser preview of the emails along with their associated data. You may also download the email as an `.eml` file.
+The mail watcher allows you to view an in-browser preview of [emails](/docs/{{version}}/mail) sent by your application along with their associated data. You may also download the email as an `.eml` file.
### Model Watcher
-The model watcher records model changes whenever an Eloquent `created`, `updated`, `restored`, or `deleted` event is dispatched. You may specify which model events should be recorded via the watcher's `events` option:
+The model watcher records model changes whenever an Eloquent [model event](/docs/{{version}}/eloquent#events) is dispatched. You may specify which model events should be recorded via the watcher's `events` option:
'watchers' => [
Watchers\ModelWatcher::class => [
@@ -337,12 +344,12 @@ The model watcher records model changes whenever an Eloquent `created`, `updated
### Notification Watcher
-The notification watcher records all notifications sent by your application. If the notification triggers an email and you have the mail watcher enabled, the email will also be available for preview on the mail watcher screen.
+The notification watcher records all [notifications](/docs/{{version}}/notifications) sent by your application. If the notification triggers an email and you have the mail watcher enabled, the email will also be available for preview on the mail watcher screen.
### Query Watcher
-The query watcher records the raw SQL, bindings, and execution time for all queries that are executed by your application. The watcher also tags any queries slower than 100ms as `slow`. You may customize the slow query threshold using the watcher's `slow` option:
+The query watcher records the raw SQL, bindings, and execution time for all queries that are executed by your application. The watcher also tags any queries slower than 100 milliseconds as `slow`. You may customize the slow query threshold using the watcher's `slow` option:
'watchers' => [
Watchers\QueryWatcher::class => [
@@ -355,12 +362,12 @@ The query watcher records the raw SQL, bindings, and execution time for all quer
### Redis Watcher
-The Redis watcher records all Redis commands executed by your application. If you are using Redis for caching, cache commands will also be recorded by the Redis Watcher.
+The Redis watcher records all [Redis](/docs/{{version}}/redis) commands executed by your application. If you are using Redis for caching, cache commands will also be recorded by the Redis watcher.
### Request Watcher
-The request watcher records the request, headers, session, and response data associated with any requests handled by the application. You may limit your response data via the `size_limit` (in KB) option:
+The request watcher records the request, headers, session, and response data associated with any requests handled by the application. You may limit your recorded response data via the `size_limit` (in kilobytes) option:
'watchers' => [
Watchers\RequestWatcher::class => [
@@ -373,17 +380,17 @@ The request watcher records the request, headers, session, and response data ass
### Schedule Watcher
-The schedule watcher records the command and output of any scheduled tasks run by your application.
+The schedule watcher records the command and output of any [scheduled tasks](/docs/{{version}}/scheduling) run by your application.
### View Watcher
-The view watcher records the view name, path, data, and "composers" used when rendering views.
+The view watcher records the [view](/docs/{{version}}/views) name, path, data, and "composers" used when rendering views.
## Displaying User Avatars
-The Telescope dashboard displays the user avatar for the user that was logged in when a given entry was saved. By default, Telescope will retrieve avatars using the Gravatar web service. However, you may customize the avatar URL by registering a callback in your `TelescopeServiceProvider`. The callback will receive the user's ID and email address and should return the user's avatar image URL:
+The Telescope dashboard displays the user avatar for the user that was authenticated when a given entry was saved. By default, Telescope will retrieve avatars using the Gravatar web service. However, you may customize the avatar URL by registering a callback in your `App\Providers\TelescopeServiceProvider` class. The callback will receive the user's ID and email address and should return the user's avatar image URL:
use App\Models\User;
use Laravel\Telescope\Telescope;
@@ -395,6 +402,8 @@ The Telescope dashboard displays the user avatar for the user that was logged in
*/
public function register()
{
+ // ...
+
Telescope::avatar(function ($id, $email) {
return '/avatars/'.User::find($id)->avatar_path;
});
diff --git a/testing.md b/testing.md
index 0b5650a50f5..224634a60ca 100644
--- a/testing.md
+++ b/testing.md
@@ -10,33 +10,38 @@
Laravel is built with testing in mind. In fact, support for testing with PHPUnit is included out of the box and a `phpunit.xml` file is already set up for your application. The framework also ships with convenient helper methods that allow you to expressively test your applications.
-By default, your application's `tests` directory contains two directories: `Feature` and `Unit`. Unit tests are tests that focus on a very small, isolated portion of your code. In fact, most unit tests probably focus on a single method. Feature tests may test a larger portion of your code, including how several objects interact with each other or even a full HTTP request to a JSON endpoint.
+By default, your application's `tests` directory contains two directories: `Feature` and `Unit`. Unit tests are tests that focus on a very small, isolated portion of your code. In fact, most unit tests probably focus on a single method. Tests within your "Unit" test directory do not boot your Laravel application and therefore are unable to access your application's database or other framework services.
-An `ExampleTest.php` file is provided in both the `Feature` and `Unit` test directories. After installing a new Laravel application, run `vendor/bin/phpunit` on the command line to run your tests.
+Feature tests may test a larger portion of your code, including how several objects interact with each other or even a full HTTP request to a JSON endpoint. **Generally, most of your tests should be feature tests. These types of tests provide the most confidence that your system as a whole is functioning as intended.**
+
+An `ExampleTest.php` file is provided in both the `Feature` and `Unit` test directories. After installing a new Laravel application, execute the `vendor/bin/phpunit` or `php artisan test` commands to run your tests.
## Environment
-When running tests via `vendor/bin/phpunit`, Laravel will automatically set the configuration environment to `testing` because of the environment variables defined in the `phpunit.xml` file. Laravel also automatically configures the session and cache to the `array` driver while testing, meaning no session or cache data will be persisted while testing.
+When running tests via `vendor/bin/phpunit`, Laravel will automatically set the [configuration environment](/docs/{{version}}/configuration#environment-configuration) to `testing` because of the environment variables defined in the `phpunit.xml` file. Laravel also automatically configures the session and cache to the `array` driver while testing, meaning no session or cache data will be persisted while testing.
+
+You are free to define other testing environment configuration values as necessary. The `testing` environment variables may be configured in your application's `phpunit.xml` file, but make sure to clear your configuration cache using the `config:clear` Artisan command before running your tests!
-You are free to define other testing environment configuration values as necessary. The `testing` environment variables may be configured in the `phpunit.xml` file, but make sure to clear your configuration cache using the `config:clear` Artisan command before running your tests!
+
+#### The `.env.testing` Environment File
-In addition, you may create a `.env.testing` file in the root of your project. This file will override the `.env` file when running PHPUnit tests or executing Artisan commands with the `--env=testing` option.
+In addition, you may create a `.env.testing` file in the root of your project. This file will be used instead of the `.env` file when running PHPUnit tests or executing Artisan commands with the `--env=testing` option.
## Creating & Running Tests
-To create a new test case, use the `make:test` Artisan command:
+To create a new test case, use the `make:test` Artisan command. By default, tests will be placed in the `tests/Feature` directory:
- // Create a test in the Feature directory...
php artisan make:test UserTest
- // Create a test in the Unit directory...
+If you would like to create a test within the `tests/Unit` directory, you may use the `--unit` option when executing the `make:test` command:
+
php artisan make:test UserTest --unit
> {tip} Test stubs may be customized using [stub publishing](/docs/{{version}}/artisan#stub-customization)
-Once the test has been generated, you may define test methods as you normally would using PHPUnit. To run your tests, execute the `phpunit` or `artisan test` command from your terminal:
+Once the test has been generated, you may define test methods as you normally would using [PHPUnit](https://phpunit.de). To run your tests, execute the `vendor/bin/phpunit` or `php artisan test` command from your terminal:
## Introduction
-Laravel provides several helpers to assist you in generating URLs for your application. These are mainly helpful when building links in your templates and API responses, or when generating redirect responses to another part of your application.
+Laravel provides several helpers to assist you in generating URLs for your application. These helpers are primarily helpful when building links in your templates and API responses, or when generating redirect responses to another part of your application.
## The Basics
-
-### Generating Basic URLs
+
+### Generating URLs
-The `url` helper may be used to generate arbitrary URLs for your application. The generated URL will automatically use the scheme (HTTP or HTTPS) and host from the current request:
+The `url` helper may be used to generate arbitrary URLs for your application. The generated URL will automatically use the scheme (HTTP or HTTPS) and host from the current request being handled by the application:
$post = App\Models\Post::find(1);
@@ -51,7 +51,7 @@ Each of these methods may also be accessed via the `URL` [facade](/docs/{{versio
## URLs For Named Routes
-The `route` helper may be used to generate URLs to named routes. Named routes allow you to generate URLs without being coupled to the actual URL defined on the route. Therefore, if the route's URL changes, no changes need to be made to your `route` function calls. For example, imagine your application contains a route defined like the following:
+The `route` helper may be used to generate URLs to [named routes](/docs/{{version}}/routing#named-routes). Named routes allow you to generate URLs without being coupled to the actual URL defined on the route. Therefore, if the route's URL changes, no changes need to be made to your calls to the `route` function. For example, imagine your application contains a route defined like the following:
Route::get('/post/{post}', function () {
//
@@ -63,17 +63,7 @@ To generate a URL to this route, you may use the `route` helper like so:
// http://example.com/post/1
-Any additional array parameters that do not correspond to the route's definition parameters will be added to the URL's query string:
-
- echo route('post.show', ['post' => 1, 'search' => 'rocket']);
-
- // http://example.com/post/1?search=rocket
-
-You will often be generating URLs using the primary key of [Eloquent models](/docs/{{version}}/eloquent). For this reason, you may pass Eloquent models as parameter values. The `route` helper will automatically extract the model's primary key:
-
- echo route('post.show', ['post' => $post]);
-
-The `route` helper may also be used to generate URLs for routes with multiple parameters:
+Of course, the `route` helper may also be used to generate URLs for routes with multiple parameters:
Route::get('/post/{post}/comment/{comment}', function () {
//
@@ -83,6 +73,19 @@ The `route` helper may also be used to generate URLs for routes with multiple pa
// http://example.com/post/1/comment/3
+Any additional array elements that do not correspond to the route's definition parameters will be added to the URL's query string:
+
+ echo route('post.show', ['post' => 1, 'search' => 'rocket']);
+
+ // http://example.com/post/1?search=rocket
+
+
+#### Eloquent Models
+
+You will often be generating URLs using the primary key of [Eloquent models](/docs/{{version}}/eloquent). For this reason, you may pass Eloquent models as parameter values. The `route` helper will automatically extract the model's primary key:
+
+ echo route('post.show', ['post' => $post]);
+
### Signed URLs
@@ -94,7 +97,7 @@ For example, you might use signed URLs to implement a public "unsubscribe" link
return URL::signedRoute('unsubscribe', ['user' => 1]);
-If you would like to generate a temporary signed route URL that expires, you may use the `temporarySignedRoute` method:
+If you would like to generate a temporary signed route URL that expires after a specified amount of time, you may use the `temporarySignedRoute` method. When Laravel validates a temporary signed route URL, it will ensure that the expiration timestamp that is encoded into the signed URL has not elapsed:
use Illuminate\Support\Facades\URL;
@@ -117,7 +120,7 @@ To verify that an incoming request has a valid signature, you should call the `h
// ...
})->name('unsubscribe');
-Alternatively, you may assign the `Illuminate\Routing\Middleware\ValidateSignature` middleware to the route. If it is not already present, you should assign this middleware a key in your HTTP kernel's `routeMiddleware` array:
+Alternatively, you may assign the `Illuminate\Routing\Middleware\ValidateSignature` [middleware](/docs/{{version}}/middleware) to the route. If it is not already present, you should assign this middleware a key in your HTTP kernel's `routeMiddleware` array:
/**
* The application's route middleware.
@@ -130,7 +133,7 @@ Alternatively, you may assign the `Illuminate\Routing\Middleware\ValidateSignatu
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
];
-Once you have registered the middleware in your kernel, you may attach it to a route. If the incoming request does not have a valid signature, the middleware will automatically return a `403` error response:
+Once you have registered the middleware in your kernel, you may attach it to a route. If the incoming request does not have a valid signature, the middleware will automatically return a `403` HTTP response:
Route::post('/unsubscribe/{user}', function (Request $request) {
// ...
@@ -145,7 +148,7 @@ The `action` function generates a URL for the given controller action:
$url = action([HomeController::class, 'index']);
-If the controller method accepts route parameters, you may pass them as the second argument to the function:
+If the controller method accepts route parameters, you may pass an associative array of route parameters as the second argument to the function:
$url = action([UserController::class, 'profile'], ['id' => 1]);
@@ -169,6 +172,13 @@ It is cumbersome to always pass the `locale` every time you call the `route` hel
class SetDefaultLocaleForUrls
{
+ /**
+ * Handle the incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @param \Closure $next
+ * @return \Illuminate\Http\Response
+ */
public function handle($request, Closure $next)
{
URL::defaults(['locale' => $request->user()->locale]);
diff --git a/valet.md b/valet.md
index 640453c0953..af580d421bd 100644
--- a/valet.md
+++ b/valet.md
@@ -1,14 +1,16 @@
# Laravel Valet
- [Introduction](#introduction)
- - [Valet Or Homestead](#valet-or-homestead)
- [Installation](#installation)
- - [Upgrading](#upgrading)
+ - [Upgrading Valet](#upgrading-valet)
- [Serving Sites](#serving-sites)
- [The "Park" Command](#the-park-command)
- [The "Link" Command](#the-link-command)
- [Securing Sites With TLS](#securing-sites)
- [Sharing Sites](#sharing-sites)
+ - [Sharing Sites Via Ngrok](#sharing-sites-via-ngrok)
+ - [Sharing Sites Via Expose](#sharing-sites-via-expose)
+ - [Sharing Sites On Your Local Network](#sharing-sites-on-your-local-network)
- [Site Specific Environment Variables](#site-specific-environment-variables)
- [Proxying Services](#proxying-services)
- [Custom Valet Drivers](#custom-valet-drivers)
@@ -19,11 +21,9 @@
## Introduction
-Valet is a Laravel development environment for Mac minimalists. No Vagrant, no `/etc/hosts` file. You can even share your sites publicly using local tunnels. _Yeah, we like it too._
+Valet is a Laravel development environment for macOS minimalists. Laravel Valet configures your Mac to always run [Nginx](https://www.nginx.com/) in the background when your machine starts. Then, using [DnsMasq](https://en.wikipedia.org/wiki/Dnsmasq), Valet proxies all requests on the `*.test` domain to point to sites installed on your local machine.
-Laravel Valet configures your Mac to always run [Nginx](https://www.nginx.com/) in the background when your machine starts. Then, using [DnsMasq](https://en.wikipedia.org/wiki/Dnsmasq), Valet proxies all requests on the `*.test` domain to point to sites installed on your local machine.
-
-In other words, a blazing fast Laravel development environment that uses roughly 7 MB of RAM. Valet isn't a complete replacement for Vagrant or Homestead, but provides a great alternative if you want flexible basics, prefer extreme speed, or are working on a machine with a limited amount of RAM.
+In other words, Valet is a blazing fast Laravel development environment that uses roughly 7 MB of RAM. Valet isn't a complete replacement for [Sail](/docs/{{version}}/sail) or [Homestead](/docs/{{version}}/homestead), but provides a great alternative if you want flexible basics, prefer extreme speed, or are working on a machine with a limited amount of RAM.
Out of the box, Valet support includes, but is not limited to:
@@ -61,41 +61,35 @@ Out of the box, Valet support includes, but is not limited to:
However, you may extend Valet with your own [custom drivers](#custom-valet-drivers).
-
-### Valet Or Homestead
+
+## Installation
-As you may know, Laravel offers [Homestead](/docs/{{version}}/homestead), another local Laravel development environment. Homestead and Valet differ in regards to their intended audience and their approach to local development. Homestead offers an entire Ubuntu virtual machine with automated Nginx configuration. Homestead is a wonderful choice if you want a fully virtualized Linux development environment or are on Windows / Linux.
+> {note} Valet requires macOS and [Homebrew](https://brew.sh/). Before installation, you should make sure that no other programs such as Apache or Nginx are binding to your local machine's port 80.
-Valet only supports Mac, and requires you to install PHP and a database server directly onto your local machine. This is easily achieved by using [Homebrew](https://brew.sh/) with commands like `brew install php` and `brew install mysql`. Valet provides a blazing fast local development environment with minimal resource consumption, so it's great for developers who only require PHP / MySQL and do not need a fully virtualized development environment.
+To get started, you first need to ensure that Homebrew is up to date using the `update` command:
-Both Valet and Homestead are great choices for configuring your Laravel development environment. Which one you choose will depend on your personal taste and your team's needs.
+ brew update
-
-## Installation
+Next, you should use Homebrew to install PHP:
-**Valet requires macOS and [Homebrew](https://brew.sh/). Before installation, you should make sure that no other programs such as Apache or Nginx are binding to your local machine's port 80.**
+ brew install php
-
-- Install or update [Homebrew](https://brew.sh/) to the latest version using `brew update`.
-- Install PHP 8.0 using Homebrew via `brew install php`.
-- Install [Composer](https://getcomposer.org).
-- Install Valet with Composer via `composer global require laravel/valet`. Make sure the `~/.composer/vendor/bin` directory is in your system's "PATH".
-- Run the `valet install` command. This will configure and install Valet and DnsMasq, and register Valet's daemon to launch when your system starts.
-
+After installing PHP, you are ready to install the [Composer package manager](https://getcomposer.org). In addition, you should make sure the `~/.composer/vendor/bin` directory is in your system's "PATH". After Composer has been installed, you may install Laravel Valet as a global Composer package:
-Once Valet is installed, try pinging any `*.test` domain on your terminal using a command such as `ping foobar.test`. If Valet is installed correctly you should see this domain responding on `127.0.0.1`.
+ composer global require laravel/valet
-Valet will automatically start its daemon each time your machine boots. There is no need to run `valet start` or `valet install` ever again once the initial Valet installation is complete.
+Finally, you may execute Valet's `install` command. This will configure and install Valet and DnsMasq. In addition, the Valet daemon will be configured to launch when your system starts:
-
-#### Database
+ valet install
+
+Once Valet is installed, try pinging any `*.test` domain on your terminal using a command such as `ping foobar.test`. If Valet is installed correctly you should see this domain responding on `127.0.0.1`.
-If you need a database, try MySQL by running `brew install mysql@5.7` on your command line. Once MySQL has been installed, you may start it using the `brew services start mysql@5.7` command. You can then connect to the database at `127.0.0.1` using the `root` username and an empty string for the password.
+Valet will automatically start its daemon each time your machine boots. There is no need to run `valet start` or `valet install` ever again once the initial Valet installation is complete.
#### PHP Versions
-Valet allows you to switch PHP versions using the `valet use php@version` command. Valet will install the specified PHP version via Brew if it is not already installed:
+Valet allows you to switch PHP versions using the `valet use php@version` command. Valet will install the specified PHP version via Homebrew if it is not already installed:
valet use php@7.2
@@ -103,84 +97,110 @@ Valet allows you to switch PHP versions using the `valet use php@version` comman
> {note} Valet only serves one PHP version at a time, even if you have multiple PHP versions installed.
+
+#### Database
+
+If your application needs a database, check out [DBngin](https://dbngin.com). DBngin provides a free, all-in-one database management tool that includes MySQL, PostgreSQL, and Redis. After DBngin has been installed, you can connect to your database at `127.0.0.1` using the `root` username and an empty string for the password.
+
#### Resetting Your Installation
If you are having trouble getting your Valet installation to run properly, executing the `composer global update` command followed by `valet install` will reset your installation and can solve a variety of problems. In rare cases it may be necessary to "hard reset" Valet by executing `valet uninstall --force` followed by `valet install`.
-
-### Upgrading
+
+### Upgrading Valet
-You may update your Valet installation using the `composer global update` command in your terminal. After upgrading, it is good practice to run the `valet install` command so Valet can make additional upgrades to your configuration files if necessary.
+You may update your Valet installation by executing the `composer global update` command in your terminal. After upgrading, it is good practice to run the `valet install` command so Valet can make additional upgrades to your configuration files if necessary.
## Serving Sites
-Once Valet is installed, you're ready to start serving sites. Valet provides two commands to help you serve your Laravel sites: `park` and `link`.
+Once Valet is installed, you're ready to start serving your Laravel applications. Valet provides two commands to help you serve your applications: `park` and `link`.
-#### The `park` Command
+### The `park` Command
-
-- Create a new directory on your Mac by running something like `mkdir ~/Sites`. Next, `cd ~/Sites` and run `valet park`. This command will register your current working directory as a path that Valet should search for sites.
-- Next, create a new Laravel site within this directory: `laravel new blog`.
-- Open `http://blog.test` in your browser.
-
+The `park` command registers a directory on your machine that contains your applications. Once the directory has been "parked" with Valet, all of the directories within that directory will be accessible in your web browser at `http://.test`:
+
+ cd ~/Sites
+
+ valet park
-**That's all there is to it.** Now, any Laravel project you create within your "parked" directory will automatically be served using the `http://folder-name.test` convention.
+That's all there is to it. Now, any application you create within your "parked" directory will automatically be served using the `http://.test` convention. So, if your parked directory contains a directory named "laravel", the application within that directory will be accessible at `http://laravel.test`.
-#### The `link` Command
+### The `link` Command
-The `link` command may also be used to serve your Laravel sites. This command is useful if you want to serve a single site in a directory and not the entire directory.
+The `link` command can also be used to serve your Laravel applications. This command is useful if you want to serve a single site in a directory and not the entire directory:
-
-- To use the command, navigate to one of your projects and run `valet link app-name` in your terminal. Valet will create a symbolic link in `~/.config/valet/Sites` which points to your current working directory.
-- After running the `link` command, you can access the site in your browser at `http://app-name.test`.
-
+ cd ~/Sites/laravel
+
+ valet link
+
+Once an application has been linked to Valet using the `link` command, you may access the application using its directory name. So, the site that was linked in the example above may be accessed at `http://laravel.test`.
+
+If you would like to serve the application at a different hostname, you may pass the hostname to the `link` command. For example, you may run the following command to make an application available at `http://application.test`:
-To see a listing of all of your linked directories, run the `valet links` command. You may use `valet unlink app-name` to destroy the symbolic link.
+ cd ~/Sites/laravel
-> {tip} You can use `valet link` to serve the same project from multiple (sub)domains. To add a subdomain or another domain to your project run `valet link subdomain.app-name` from the project folder.
+ valet link application
+
+You may execute the `links` command to display a list of all of your linked directories:
+
+ valet links
+
+The `unlink` command may be used to destroy the symbolic link for a site:
+
+ cd ~/Sites/laravel
+
+ valet unlink
-#### Securing Sites With TLS
+### Securing Sites With TLS
-By default, Valet serves sites over plain HTTP. However, if you would like to serve a site over encrypted TLS using HTTP/2, use the `secure` command. For example, if your site is being served by Valet on the `laravel.test` domain, you should run the following command to secure it:
+By default, Valet serves sites over HTTP. However, if you would like to serve a site over encrypted TLS using HTTP/2, you may use the `secure` command. For example, if your site is being served by Valet on the `laravel.test` domain, you should run the following command to secure it:
valet secure laravel
-To "unsecure" a site and revert back to serving its traffic over plain HTTP, use the `unsecure` command. Like the `secure` command, this command accepts the host name that you wish to unsecure:
+To "unsecure" a site and revert back to serving its traffic over plain HTTP, use the `unsecure` command. Like the `secure` command, this command accepts the hostname that you wish to unsecure:
valet unsecure laravel
## Sharing Sites
-Valet even includes a command to share your local sites with the world, providing an easy way to test your site on mobile devices or share it with team members and clients. No additional software installation is required once Valet is installed.
+Valet even includes a command to share your local sites with the world, providing an easy way to test your site on mobile devices or share it with team members and clients.
### Sharing Sites Via Ngrok
-To share a site, navigate to the site's directory in your terminal and run the `valet share` command. A publicly accessible URL will be inserted into your clipboard and is ready to paste directly into your browser or share with your team.
+To share a site, navigate to the site's directory in your terminal and run Valet's `share` command. A publicly accessible URL will be inserted into your clipboard and is ready to paste directly into your browser or share with your team:
+
+ cd ~/Sites/laravel
-To stop sharing your site, hit `Control + C` to cancel the process.
+ valet share
-> {tip} You may pass additional parameters to the share command, such as `valet share --region=eu`. For more information, consult the [ngrok documentation](https://ngrok.com/docs).
+To stop sharing your site, you may press `Control + C`.
+
+> {tip} You may pass additional Ngrok parameters to the share command, such as `valet share --region=eu`. For more information, consult the [ngrok documentation](https://ngrok.com/docs).
### Sharing Sites Via Expose
-If you have [Expose](https://beyondco.de/docs/expose) installed, you can share your site by navigating to the site's directory in your terminal and running the `expose` command. Consult the expose documentation for additional command-line parameters it supports. After sharing the site, Expose will display the sharable URL that you may use on your other devices or amongst team members.
+If you have [Expose](https://beyondco.de/docs/expose) installed, you can share your site by navigating to the site's directory in your terminal and running the `expose` command. Consult the [Expose documentation](https://beyondco.de/docs/expose/introduction) for information regarding the additional command-line parameters it supports. After sharing the site, Expose will display the sharable URL that you may use on your other devices or amongst team members:
+
+ cd ~/Sites/laravel
-To stop sharing your site, hit `Control + C` to cancel the process.
+ valet expose
+
+To stop sharing your site, you may press `Control + C`.
### Sharing Sites On Your Local Network
-Valet restricts incoming traffic to the internal `127.0.0.1` interface by default. This way your development machine isn't exposed to security risks from the Internet.
+Valet restricts incoming traffic to the internal `127.0.0.1` interface by default so that your development machine isn't exposed to security risks from the Internet.
-If you wish to allow other devices on your local network to access the Valet sites on your machine via your machine's IP address (eg: `192.168.1.10/app-name.test`), you will need to manually edit the appropriate Nginx configuration file for that site to remove the restriction on the `listen` directive by removing the `127.0.0.1:` prefix on the directive for ports 80 and 443.
+If you wish to allow other devices on your local network to access the Valet sites on your machine via your machine's IP address (eg: `192.168.1.10/application.test`), you will need to manually edit the appropriate Nginx configuration file for that site to remove the restriction on the `listen` directive. You should remove the `127.0.0.1:` prefix on the `listen` directive for ports 80 and 443.
If you have not run `valet secure` on the project, you can open up network access for all non-HTTPS sites by editing the `/usr/local/etc/nginx/valet/valet.conf` file. However, if you're serving the project site over HTTPS (you have run `valet secure` for the site) then you should edit the `~/.config/valet/Nginx/app-name.test` file.
@@ -189,19 +209,17 @@ Once you have updated your Nginx configuration, run the `valet restart` command
## Site Specific Environment Variables
-Some applications using other frameworks may depend on server environment variables but do not provide a way for those variables to be configured within your project. Valet allows you to configure site specific environment variables by adding a `.valet-env.php` file within the root of your project. These variables will be added to the `$_SERVER` global array:
+Some applications using other frameworks may depend on server environment variables but do not provide a way for those variables to be configured within your project. Valet allows you to configure site specific environment variables by adding a `.valet-env.php` file within the root of your project. This file should return an array of site / environment variable pairs which will be added to the global `$_SERVER` array for each site specified in the array:
[
+ // Set $_SERVER['key'] to "value" for the laravel.test site...
+ 'laravel' => [
'key' => 'value',
],
- ];
- // Set $_SERVER['key'] to "value" for all sites...
- return [
+ // Set $_SERVER['key'] to "value" for all sites...
'*' => [
'key' => 'value',
],
@@ -214,24 +232,26 @@ Sometimes you may wish to proxy a Valet domain to another service on your local
To solve this, you may use the `proxy` command to generate a proxy. For example, you may proxy all traffic from `http://elasticsearch.test` to `http://127.0.0.1:9200`:
- valet proxy elasticsearch http://127.0.0.1:9200
+```bash
+valet proxy elasticsearch http://127.0.0.1:9200
+```
You may remove a proxy using the `unproxy` command:
valet unproxy elasticsearch
-You may use the `proxies` command to list all site configuration that are proxied:
+You may use the `proxies` command to list all site configurations that are proxied:
valet proxies
## Custom Valet Drivers
-You can write your own Valet "driver" to serve PHP applications running on another framework or CMS that is not natively supported by Valet. When you install Valet, a `~/.config/valet/Drivers` directory is created which contains a `SampleValetDriver.php` file. This file contains a sample driver implementation to demonstrate how to write a custom driver. Writing a driver only requires you to implement three methods: `serves`, `isStaticFile`, and `frontControllerPath`.
+You can write your own Valet "driver" to serve PHP applications running on a framework or CMS that is not natively supported by Valet. When you install Valet, a `~/.config/valet/Drivers` directory is created which contains a `SampleValetDriver.php` file. This file contains a sample driver implementation to demonstrate how to write a custom driver. Writing a driver only requires you to implement three methods: `serves`, `isStaticFile`, and `frontControllerPath`.
All three methods receive the `$sitePath`, `$siteName`, and `$uri` values as their arguments. The `$sitePath` is the fully qualified path to the site being served on your machine, such as `/Users/Lisa/Sites/my-project`. The `$siteName` is the "host" / "site name" portion of the domain (`my-project`). The `$uri` is the incoming request URI (`/foo/bar`).
-Once you have completed your custom Valet driver, place it in the `~/.config/valet/Drivers` directory using the `FrameworkValetDriver.php` naming convention. For example, if you are writing a custom valet driver for WordPress, your file name should be `WordPressValetDriver.php`.
+Once you have completed your custom Valet driver, place it in the `~/.config/valet/Drivers` directory using the `FrameworkValetDriver.php` naming convention. For example, if you are writing a custom valet driver for WordPress, your filename should be `WordPressValetDriver.php`.
Let's take a look at a sample implementation of each method your custom Valet driver should implement.
@@ -240,7 +260,7 @@ Let's take a look at a sample implementation of each method your custom Valet dr
The `serves` method should return `true` if your driver should handle the incoming request. Otherwise, the method should return `false`. So, within this method you should attempt to determine if the given `$sitePath` contains a project of the type you are trying to serve.
-For example, let's pretend we are writing a `WordPressValetDriver`. Our `serves` method might look something like this:
+For example, let's imagine we are writing a `WordPressValetDriver`. Our `serves` method might look something like this:
/**
* Determine if the driver serves the request.
@@ -282,7 +302,7 @@ The `isStaticFile` should determine if the incoming request is for a file that i
#### The `frontControllerPath` Method
-The `frontControllerPath` method should return the fully qualified path to your application's "front controller", which is typically your "index.php" file or equivalent:
+The `frontControllerPath` method should return the fully qualified path to your application's "front controller", which is typically an "index.php" file or equivalent:
/**
* Get the fully resolved path to the application's front controller.
@@ -300,7 +320,7 @@ The `frontControllerPath` method should return the fully qualified path to your
### Local Drivers
-If you would like to define a custom Valet driver for a single application, create a `LocalValetDriver.php` in the application's root directory. Your custom driver may extend the base `ValetDriver` class or extend an existing application specific driver such as the `LaravelValetDriver`:
+If you would like to define a custom Valet driver for a single application, create a `LocalValetDriver.php` file in the application's root directory. Your custom driver may extend the base `ValetDriver` class or extend an existing application specific driver such as the `LaravelValetDriver`:
class LocalValetDriver extends LaravelValetDriver
{
@@ -342,28 +362,70 @@ Command | Description
`valet restart` | Restart the Valet daemon.
`valet start` | Start the Valet daemon.
`valet stop` | Stop the Valet daemon.
-`valet trust` | Add sudoers files for Brew and Valet to allow Valet commands to be run without prompting for passwords.
-`valet uninstall` | Uninstall Valet: Shows instructions for manual uninstall; or pass the `--force` parameter to aggressively delete all of Valet.
+`valet trust` | Add sudoers files for Brew and Valet to allow Valet commands to be run without prompting for your password.
+`valet uninstall` | Uninstall Valet: shows instructions for manual uninstall. Pass the `--force` option to aggressively delete all of Valet's resources.
## Valet Directories & Files
You may find the following directory and file information helpful while troubleshooting issues with your Valet environment:
-File / Path | Description
---------- | -----------
-`~/.config/valet/` | Contains all of Valet's configuration. You may wish to maintain a backup of this folder.
-`~/.config/valet/dnsmasq.d/` | Contains DNSMasq's configuration.
-`~/.config/valet/Drivers/` | Contains custom Valet drivers.
-`~/.config/valet/Extensions/` | Contains custom Valet extensions / commands.
-`~/.config/valet/Nginx/` | Contains all Valet generated Nginx site configurations. These files are rebuilt when running the `install`, `secure`, and `tld` commands.
-`~/.config/valet/Sites/` | Contains all symbolic links for linked projects.
-`~/.config/valet/config.json` | Valet's master configuration file
-`~/.config/valet/valet.sock` | The PHP-FPM socket used by Valet's Nginx configuration. This will only exist if PHP is running properly.
-`~/.config/valet/Log/fpm-php.www.log` | User log for PHP errors.
-`~/.config/valet/Log/nginx-error.log` | User log for Nginx errors.
-`/usr/local/var/log/php-fpm.log` | System log for PHP-FPM errors.
-`/usr/local/var/log/nginx` | Contains Nginx access and error logs.
-`/usr/local/etc/php/X.X/conf.d` | Contains `*.ini` files for various PHP configuration settings.
-`/usr/local/etc/php/X.X/php-fpm.d/valet-fpm.conf` | PHP-FPM pool configuration file.
-`~/.composer/vendor/laravel/valet/cli/stubs/secure.valet.conf` | The default Nginx configuration used for building site certificates.
+#### `~/.config/valet`
+
+Contains all of Valet's configuration. You may wish to maintain a backup of this directory.
+
+#### `~/.config/valet/dnsmasq.d/`
+
+This directory contains DNSMasq's configuration.
+
+#### `~/.config/valet/Drivers/`
+
+This directory contains Valet's drivers. Drivers determine how a particular framework / CMS is served.
+
+#### `~/.config/valet/Extensions/`
+
+This directory contains custom Valet extensions / commands.
+
+#### `~/.config/valet/Nginx/`
+
+This directory contains all of Valet's Nginx site configurations. These files are rebuilt when running the `install`, `secure`, and `tld` commands.
+
+#### `~/.config/valet/Sites/`
+
+This directory contains all of the symbolic links for your [linked projects](#the-link-command).
+
+#### `~/.config/valet/config.json`
+
+This file is Valet's master configuration file.
+
+#### `~/.config/valet/valet.sock`
+
+This file is the PHP-FPM socket used by Valet's Nginx installation. This will only exist if PHP is running properly.
+
+#### `~/.config/valet/Log/fpm-php.www.log`
+
+This file is the user log for PHP errors.
+
+#### `~/.config/valet/Log/nginx-error.log`
+
+This file is the user log for Nginx errors.
+
+#### `/usr/local/var/log/php-fpm.log`
+
+This file is the system log for PHP-FPM errors.
+
+#### `/usr/local/var/log/nginx`
+
+This directory contains the Nginx access and error logs.
+
+#### `/usr/local/etc/php/X.X/conf.d`
+
+This directory contains the `*.ini` files for various PHP configuration settings.
+
+#### `/usr/local/etc/php/X.X/php-fpm.d/valet-fpm.conf`
+
+This file is the PHP-FPM pool configuration file.
+
+#### `~/.composer/vendor/laravel/valet/cli/stubs/secure.valet.conf`
+
+This file is the default Nginx configuration used for building SSL certificates for your sites.
diff --git a/validation.md b/validation.md
index 9f99cbe84e8..19b0636951d 100644
--- a/validation.md
+++ b/validation.md
@@ -6,37 +6,41 @@
- [Creating The Controller](#quick-creating-the-controller)
- [Writing The Validation Logic](#quick-writing-the-validation-logic)
- [Displaying The Validation Errors](#quick-displaying-the-validation-errors)
+ - [Repopulating Forms](#repopulating-forms)
- [A Note On Optional Fields](#a-note-on-optional-fields)
- [Form Request Validation](#form-request-validation)
- [Creating Form Requests](#creating-form-requests)
- [Authorizing Form Requests](#authorizing-form-requests)
- [Customizing The Error Messages](#customizing-the-error-messages)
- - [Customizing The Validation Attributes](#customizing-the-validation-attributes)
- - [Prepare Input For Validation](#prepare-input-for-validation)
+ - [Preparing Input For Validation](#preparing-input-for-validation)
- [Manually Creating Validators](#manually-creating-validators)
- [Automatic Redirection](#automatic-redirection)
- [Named Error Bags](#named-error-bags)
+ - [Customizing The Error Messages](#manual-customizing-the-error-messages)
- [After Validation Hook](#after-validation-hook)
- [Working With Error Messages](#working-with-error-messages)
- - [Custom Error Messages](#custom-error-messages)
+ - [Specifying Custom Messages In Language Files](#specifying-custom-messages-in-language-files)
+ - [Specifying Attributes In Language Files](#specifying-attribute-in-language-files)
+ - [Specifying Values In Language Files](#specifying-values-replacements-in-language-files)
- [Available Validation Rules](#available-validation-rules)
- [Conditionally Adding Rules](#conditionally-adding-rules)
- [Validating Arrays](#validating-arrays)
- [Custom Validation Rules](#custom-validation-rules)
- [Using Rule Objects](#using-rule-objects)
- [Using Closures](#using-closures)
- - [Using Extensions](#using-extensions)
- - [Implicit Extensions](#implicit-extensions)
+ - [Implicit Rules](#implicit-rules)
## Introduction
-Laravel provides several different approaches to validate your application's incoming data. By default, Laravel's base controller class uses a `ValidatesRequests` trait which provides a convenient method to validate incoming HTTP requests with a variety of powerful validation rules.
+Laravel provides several different approaches to validate your application's incoming data. It is most common to use the `validate` method available on all incoming HTTP requests. However, we will discuss other approaches to validation as well.
+
+Laravel includes a wide variety of convenient validation rules that you may apply to data, even providing the ability to validate if values are unique in a given database table. We'll cover each of these validation rules in detail so that you are familiar with all of Laravel's validation features.
## Validation Quickstart
-To learn about Laravel's powerful validation features, let's look at a complete example of validating a form and displaying the error messages back to the user.
+To learn about Laravel's powerful validation features, let's look at a complete example of validating a form and displaying the error messages back to the user. By reading this high-level overview, you'll be able to gain a good general understanding of how to validate incoming request data using Laravel:
### Defining The Routes
@@ -45,16 +49,15 @@ First, let's assume we have the following routes defined in our `routes/web.php`
use App\Http\Controllers\PostController;
- Route::get('post/create', [PostController::class, 'create']);
-
- Route::post('post', [PostController::class, 'store']);
+ Route::get('/post/create', [PostController::class, 'create']);
+ Route::post('/post', [PostController::class, 'store']);
The `GET` route will display a form for the user to create a new blog post, while the `POST` route will store the new blog post in the database.
### Creating The Controller
-Next, let's take a look at a simple controller that handles these routes. We'll leave the `store` method empty for now:
+Next, let's take a look at a simple controller that handles incoming requests to these routes. We'll leave the `store` method empty for now:
### Writing The Validation Logic
-Now we are ready to fill in our `store` method with the logic to validate the new blog post. To do this, we will use the `validate` method provided by the `Illuminate\Http\Request` object. If the validation rules pass, your code will keep executing normally; however, if validation fails, an exception will be thrown and the proper error response will automatically be sent back to the user. In the case of a traditional HTTP request, a redirect response will be generated, while a JSON response will be sent for AJAX requests.
+Now we are ready to fill in our `store` method with the logic to validate the new blog post. To do this, we will use the `validate` method provided by the `Illuminate\Http\Request` object. If the validation rules pass, your code will keep executing normally; however, if validation fails, an exception will be thrown and the proper error response will automatically be sent back to the user.
+
+If validation fails during a traditional HTTP request, a redirect response to the previous URL will be generated. If the incoming request is an XHR request, a JSON response containing the validation error messages will be returned.
To get a better understanding of the `validate` method, let's jump back into the `store` method:
/**
* Store a new blog post.
*
- * @param Request $request
- * @return Response
+ * @param \Illuminate\Http\Request $request
+ * @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
- $validatedData = $request->validate([
+ $validated = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
@@ -110,7 +115,7 @@ To get a better understanding of the `validate` method, let's jump back into the
// The blog post is valid...
}
-As you can see, we pass the desired validation rules into the `validate` method. Again, if the validation fails, the proper response will automatically be generated. If the validation passes, our controller will continue executing normally.
+As you can see, the validation rules are passed into the `validate` method. Don't worry - all available validation rules are [documented](#available-validation-rules). Again, if the validation fails, the proper response will automatically be generated. If the validation passes, our controller will continue executing normally.
Alternatively, validation rules may be specified as arrays of rules instead of a single `|` delimited string:
@@ -119,7 +124,7 @@ Alternatively, validation rules may be specified as arrays of rules instead of a
'body' => ['required'],
]);
-You may use the `validateWithBag` method to validate a request and store any error messages within a [named error bag](#named-error-bags):
+In addition, you may use the `validateWithBag` method to validate a request and store any error messages within a [named error bag](#named-error-bags):
$validatedData = $request->validateWithBag('post', [
'title' => ['required', 'unique:posts', 'max:255'],
@@ -141,7 +146,7 @@ In this example, if the `unique` rule on the `title` attribute fails, the `max`
#### A Note On Nested Attributes
-If your HTTP request contains "nested" parameters, you may specify them in your validation rules using "dot" syntax:
+If the incoming HTTP request contains "nested" field data, you may specify these fields in your validation rules using "dot" syntax:
$request->validate([
'title' => 'required|unique:posts|max:255',
@@ -159,44 +164,71 @@ On the other hand, if your field name contains a literal period, you can explici
### Displaying The Validation Errors
-So, what if the incoming request parameters do not pass the given validation rules? As mentioned previously, Laravel will automatically redirect the user back to their previous location. In addition, all of the validation errors will automatically be [flashed to the session](/docs/{{version}}/session#flash-data).
-
-Again, notice that we did not have to explicitly bind the error messages to the view in our `GET` route. This is because Laravel will check for errors in the session data, and automatically bind them to the view if they are available. The `$errors` variable will be an instance of `Illuminate\Support\MessageBag`. For more information on working with this object, [check out its documentation](#working-with-error-messages).
+So, what if the incoming request fields do not pass the given validation rules? As mentioned previously, Laravel will automatically redirect the user back to their previous location. In addition, all of the validation errors and [request input](/docs/{{version}}/requests#retrieving-old-input) will automatically be [flashed to the session](/docs/{{version}}/session#flash-data).
-> {tip} The `$errors` variable is bound to the view by the `Illuminate\View\Middleware\ShareErrorsFromSession` middleware, which is provided by the `web` middleware group. **When this middleware is applied an `$errors` variable will always be available in your views**, allowing you to conveniently assume the `$errors` variable is always defined and can be safely used.
+An `$errors` variable is shared with all of your application's views by the `Illuminate\View\Middleware\ShareErrorsFromSession` middleware, which is provided by the `web` middleware group. When this middleware is applied an `$errors` variable will always be available in your views, allowing you to conveniently assume the `$errors` variable is always defined and can be safely used. The `$errors` variable will be an instance of `Illuminate\Support\MessageBag`. For more information on working with this object, [check out its documentation](#working-with-error-messages).
So, in our example, the user will be redirected to our controller's `create` method when validation fails, allowing us to display the error messages in the view:
-
+```html
+
+
+
Create Post
+
+@if ($errors->any())
+
+
+ @foreach ($errors->all() as $error)
+
{{ $error }}
+ @endforeach
+
+
+@endif
-
Create Post
+
+```
- @if ($errors->any())
-
-
- @foreach ($errors->all() as $error)
-
{{ $error }}
- @endforeach
-
-
- @endif
+
+#### Customizing The Error Messages
-
+Laravel's built-in validation rules each have an error message that is located in your application's `resources/lang/en/validation.php` file. Within this file, you will find a translation entry for each validation rule. You are free to change or modify these messages based on the needs of your application.
+
+In addition, you may copy this file to another translation language directory to translate the messages for your application's language. To learn more about Laravel localization, check out the complete [localization documentation](/docs/{{version}}/localization).
+
+
+#### XHR Requests & Validation
+
+In this example, we used a traditional form to send data to the application. However, many applications receive XHR requests from a JavaScript powered frontend. When using the `validate` method during an XHR request, Laravel will not generate a redirect response. Instead, Laravel generates a JSON response containing all of the validation errors. This JSON response will be sent with a 422 HTTP status code.
#### The `@error` Directive
-You may also use the `@error` [Blade](/docs/{{version}}/blade) directive to quickly check if validation error messages exist for a given attribute. Within an `@error` directive, you may echo the `$message` variable to display the error message:
+You may use the `@error` [Blade](/docs/{{version}}/blade) directive to quickly determine if validation error messages exist for a given attribute. Within an `@error` directive, you may echo the `$message` variable to display the error message:
+
+```html
+
+
+
+
+
+
+@error('title')
+
{{ $message }}
+@enderror
+```
+
+
+### Repopulating Forms
+
+When Laravel generates a redirect response due to a validation error, the framework will automatically [flash all of the request's input to the session](/docs/{{version}}/session#flash-data). This is done so that you may conveniently access the input during the next request and repopulate the form that the user attempted to submit.
-
+To retrieve flashed input from the previous request, invoke the `old` method on an instance of `Illuminate\Http\Request`. The `old` method will pull the previously flashed input data from the [session](/docs/{{version}}/session):
-
+ $title = $request->old('title');
-
+Laravel also provides a global `old` helper. If you are displaying old input within a [Blade template](/docs/{{version}}/blade), it is more convenient to use the `old` helper to repopulate the form. If no old input exists for the given field, `null` will be returned:
- @error('title')
-
{{ $message }}
- @enderror
+
### A Note On Optional Fields
@@ -211,22 +243,19 @@ By default, Laravel includes the `TrimStrings` and `ConvertEmptyStringsToNull` m
In this example, we are specifying that the `publish_at` field may be either `null` or a valid date representation. If the `nullable` modifier is not added to the rule definition, the validator would consider `null` an invalid date.
-
-#### AJAX Requests & Validation
-
-In this example, we used a traditional form to send data to the application. However, many applications use AJAX requests. When using the `validate` method during an AJAX request, Laravel will not generate a redirect response. Instead, Laravel generates a JSON response containing all of the validation errors. This JSON response will be sent with a 422 HTTP status code.
-
## Form Request Validation
### Creating Form Requests
-For more complex validation scenarios, you may wish to create a "form request". Form requests are custom request classes that contain validation logic. To create a form request class, use the `make:request` Artisan CLI command:
+For more complex validation scenarios, you may wish to create a "form request". Form requests are custom request classes that encapsulate their own validation and authorization logic. To create a form request class, you may use the `make:request` Artisan CLI command:
+
+ php artisan make:request StorePostRequest
- php artisan make:request StoreBlogPost
+The generated form request class will be placed in the `app/Http/Requests` directory. If this directory does not exist, it will be created when you run the `make:request` command. Each form request generated by Laravel has two methods: `authorize` and `rules`.
-The generated class will be placed in the `app/Http/Requests` directory. If this directory does not exist, it will be created when you run the `make:request` command. Let's add a few validation rules to the `rules` method:
+As you might have guessed, the `authorize` method is responsible for determining if the currently authenticated user can perform the action represented by the request, while the `rules` method returns the validation rules that should apply to the request's data:
/**
* Get the validation rules that apply to the request.
@@ -241,17 +270,17 @@ The generated class will be placed in the `app/Http/Requests` directory. If this
];
}
-> {tip} You may type-hint any dependencies you need within the `rules` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container).
+> {tip} You may type-hint any dependencies you require within the `rules` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container).
So, how are the validation rules evaluated? All you need to do is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic:
/**
- * Store the incoming blog post.
+ * Store a new blog post.
*
- * @param StoreBlogPost $request
- * @return Response
+ * @param \App\Http\Requests\StorePostRequest $request
+ * @return Illuminate\Http\Response
*/
- public function store(StoreBlogPost $request)
+ public function store(StorePostRequest $request)
{
// The incoming request is valid...
@@ -259,12 +288,12 @@ So, how are the validation rules evaluated? All you need to do is type-hint the
$validated = $request->validated();
}
-If validation fails, a redirect response will be generated to send the user back to their previous location. The errors will also be flashed to the session so they are available for display. If the request was an AJAX request, an HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors.
+If validation fails, a redirect response will be generated to send the user back to their previous location. The errors will also be flashed to the session so they are available for display. If the request was an XHR request, an HTTP response with a 422 status code will be returned to the user including a JSON representation of the validation errors.
#### Adding After Hooks To Form Requests
-If you would like to add an "after" hook to a form request, you may use the `withValidator` method. This method receives the fully constructed validator, allowing you to call any of its methods before the validation rules are actually evaluated:
+If you would like to add an "after" validation hook to a form request, you may use the `withValidator` method. This method receives the fully constructed validator, allowing you to call any of its methods before the validation rules are actually evaluated:
/**
* Configure the validator instance.
@@ -284,7 +313,9 @@ If you would like to add an "after" hook to a form request, you may use the `wit
### Authorizing Form Requests
-The form request class also contains an `authorize` method. Within this method, you may check if the authenticated user actually has the authority to update a given resource. For example, you may determine if a user actually owns a blog comment they are attempting to update:
+The form request class also contains an `authorize` method. Within this method, you may determine if the authenticated user actually has the authority to update a given resource. For example, you may determine if a user actually owns a blog comment they are attempting to update. Most likely, you will interact with your [authorization gates and policies](/docs/{{version}}/authorization) within this method:
+
+ use App\Models\Comment;
/**
* Determine if the user is authorized to make this request.
@@ -300,11 +331,11 @@ The form request class also contains an `authorize` method. Within this method,
Since all form requests extend the base Laravel request class, we may use the `user` method to access the currently authenticated user. Also note the call to the `route` method in the example above. This method grants you access to the URI parameters defined on the route being called, such as the `{comment}` parameter in the example below:
- Route::post('comment/{comment}');
+ Route::post('/comment/{comment}');
If the `authorize` method returns `false`, an HTTP response with a 403 status code will automatically be returned and your controller method will not execute.
-If you plan to have authorization logic in another part of your application, return `true` from the `authorize` method:
+If you plan to handle authorization logic for the request in another part of your application, you may simply return `true` from the `authorize` method:
/**
* Determine if the user is authorized to make this request.
@@ -337,9 +368,9 @@ You may customize the error messages used by the form request by overriding the
}
-### Customizing The Validation Attributes
+#### Customizing The Validation Attributes
-If you would like the `:attribute` portion of your validation message to be replaced with a custom attribute name, you may specify the custom names by overriding the `attributes` method. This method should return an array of attribute / name pairs:
+Many of Laravel's built-in validation rule error messages contain an `:attribute` placeholder. If you would like the `:attribute` placeholder of your validation message to be replaced with a custom attribute name, you may specify the custom names by overriding the `attributes` method. This method should return an array of attribute / name pairs:
/**
* Get custom attributes for validator errors.
@@ -353,10 +384,10 @@ If you would like the `:attribute` portion of your validation message to be repl
];
}
-
-### Prepare Input For Validation
+
+### Preparing Input For Validation
-If you need to sanitize any data from the request before you apply your validation rules, you can use the `prepareForValidation` method:
+If you need to prepare or sanitize any data from the request before you apply your validation rules, you may use the `prepareForValidation` method:
use Illuminate\Support\Str;
@@ -412,12 +443,12 @@ If you do not want to use the `validate` method on the request, you may create a
The first argument passed to the `make` method is the data under validation. The second argument is an array of the validation rules that should be applied to the data.
-After checking if the request validation failed, you may use the `withErrors` method to flash the error messages to the session. When using this method, the `$errors` variable will automatically be shared with your views after redirection, allowing you to easily display them back to the user. The `withErrors` method accepts a validator, a `MessageBag`, or a PHP `array`.
+After determining whether the request validation failed, you may use the `withErrors` method to flash the error messages to the session. When using this method, the `$errors` variable will automatically be shared with your views after redirection, allowing you to easily display them back to the user. The `withErrors` method accepts a validator, a `MessageBag`, or a PHP `array`.
### Automatic Redirection
-If you would like to create a validator instance manually but still take advantage of the automatic redirection offered by the request's `validate` method, you may call the `validate` method on an existing validator instance. If validation fails, the user will automatically be redirected or, in the case of an AJAX request, a JSON response will be returned:
+If you would like to create a validator instance manually but still take advantage of the automatic redirection offered by the HTTP request's `validate` method, you may call the `validate` method on an existing validator instance. If validation fails, the user will automatically be redirected or, in the case of an XHR request, a JSON response will be returned:
Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
@@ -434,25 +465,62 @@ You may use the `validateWithBag` method to store the error messages in a [named
### Named Error Bags
-If you have multiple forms on a single page, you may wish to name the `MessageBag` of errors, allowing you to retrieve the error messages for a specific form. Pass a name as the second argument to `withErrors`:
+If you have multiple forms on a single page, you may wish to name the `MessageBag` containing the validation errors, allowing you to retrieve the error messages for a specific form. To achieve this, pass a name as the second argument to `withErrors`:
- return redirect('register')
- ->withErrors($validator, 'login');
+ return redirect('register')->withErrors($validator, 'login');
You may then access the named `MessageBag` instance from the `$errors` variable:
{{ $errors->login->first('email') }}
+
+### Customizing The Error Messages
+
+If needed, you may use custom error messages for a validator instance instead of the default error messages. There are several ways to specify custom messages. First, you may pass the custom messages as the third argument to the `Validator::make` method:
+
+ $validator = Validator::make($input, $rules, $messages = [
+ 'required' => 'The :attribute field is required.',
+ ]);
+
+In this example, the `:attribute` placeholder will be replaced by the actual name of the field under validation. You may also utilize other placeholders in validation messages. For example:
+
+ $messages = [
+ 'same' => 'The :attribute and :other must match.',
+ 'size' => 'The :attribute must be exactly :size.',
+ 'between' => 'The :attribute value :input is not 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 message only for a specific attribute. You may do so using "dot" notation. Specify the attribute's name first, followed by the rule:
+
+ $messages = [
+ 'email.required' => 'We need to know your email address!',
+ ];
+
+
+#### Specifying Custom Attribute Values
+
+Many of Laravel's built-in error messages include an `:attribute:` placeholder that is replaced with the name of the field or attribute under validation. To customize the values used to replace these placeholders for specific fields, you may pass an array of custom attributes as the fourth argument to the `Validator::make` method:
+
+ $validator = Validator::make($input, $rules, $messages, [
+ 'email' => 'email address',
+ ]);
+
### After Validation Hook
-The validator also allows you to attach callbacks to be run after validation is completed. This allows you to easily perform further validation and even add more error messages to the message collection. To get started, use the `after` method on a validator instance:
+You may also attach callbacks to be run after validation is completed. This allows you to easily perform further validation and even add more error messages to the message collection. To get started, call the `after` method on a validator instance:
$validator = Validator::make(...);
$validator->after(function ($validator) {
if ($this->somethingElseIsInvalid()) {
- $validator->errors()->add('field', 'Something is wrong with this field!');
+ $validator->errors()->add(
+ 'field', 'Something is wrong with this field!'
+ );
}
});
@@ -507,69 +575,40 @@ The `has` method may be used to determine if any error messages exist for a give
//
}
-
-### Custom Error Messages
-
-If needed, you may use custom error messages for validation instead of the defaults. There are several ways to specify custom messages. First, you may pass the custom messages as the third argument to the `Validator::make` method:
-
- $messages = [
- 'required' => 'The :attribute field is required.',
- ];
-
- $validator = Validator::make($input, $rules, $messages);
-
-In this example, the `:attribute` placeholder will be replaced by the actual name of the field under validation. You may also utilize other placeholders in validation messages. For example:
-
- $messages = [
- 'same' => 'The :attribute and :other must match.',
- 'size' => 'The :attribute must be exactly :size.',
- 'between' => 'The :attribute value :input is not between :min - :max.',
- 'in' => 'The :attribute must be one of the following types: :values',
- ];
-
-
-#### Specifying A Custom Message For A Given Attribute
+
+### Specifying Custom Messages In Language Files
-Sometimes you may wish to specify a custom error message only for a specific field. You may do so using "dot" notation. Specify the attribute's name first, followed by the rule:
+Laravel's built-in validation rules each have an error message that is located in your application's `resources/lang/en/validation.php` file. Within this file, you will find a translation entry for each validation rule. You are free to change or modify these messages based on the needs of your application.
- $messages = [
- 'email.required' => 'We need to know your e-mail address!',
- ];
+In addition, you may copy this file to another translation language directory to translate the messages for your application's language. To learn more about Laravel localization, check out the complete [localization documentation](/docs/{{version}}/localization).
-
-#### Specifying Custom Messages In Language Files
+
+#### Custom Messages For Specific Attributes
-In most cases, you will probably specify your custom messages in a language file instead of passing them directly to the `Validator`. To do so, add your messages to `custom` array in the `resources/lang/xx/validation.php` language file.
+You may customize the error messages used for specified attribute and rule combinations within your application's validation language files. To do so, add your message customizations to the `custom` array of your application's `resources/lang/xx/validation.php` language file:
'custom' => [
'email' => [
- 'required' => 'We need to know your e-mail address!',
+ 'required' => 'We need to know your email address!',
+ 'max' => 'Your email address is too long!'
],
],
-
-#### Specifying Custom Attribute Values
+
+### Specifying Attributes In Language Files
-If you would like the `:attribute` portion of your validation message to be replaced with a custom attribute name, you may specify the custom name in the `attributes` array of your `resources/lang/xx/validation.php` language file:
+Many of Laravel's built-in error messages include an `:attribute:` placeholder that is replaced with the name of the field or attribute under validation. If you would like the `:attribute` portion of your validation message to be replaced with a custom value, you may specify the custom attribute name in the `attributes` array of your `resources/lang/xx/validation.php` language file:
'attributes' => [
'email' => 'email address',
],
-You may also pass the custom attributes as the fourth argument to the `Validator::make` method:
-
- $customAttributes = [
- 'email' => 'email address',
- ];
-
- $validator = Validator::make($input, $rules, $messages, $customAttributes);
-
-
-#### Specifying Custom Values In Language Files
+
+### Specifying Values In Language Files
-Sometimes you may need the `:value` portion of your validation message to be replaced with a custom representation of the value. For example, consider the following rule that specifies that a credit card number is required if the `payment_type` has a value of `cc`:
+Some of Laravel's built-in validation rule error messages contain a `:value` placeholder that is replaced with the current value of the request attribute. However, you may occasionally need the `:value` portion of your validation message to be replaced with a custom representation of the value. For example, consider the following rule that specifies that a credit card number is required if the `payment_type` has a value of `cc`:
- $request->validate([
+ Validator::make($request->all(), [
'credit_card_number' => 'required_if:payment_type,cc'
]);
@@ -577,7 +616,7 @@ If this validation rule fails, it will produce the following error message:
The credit card number field is required when payment type is cc.
-Instead of displaying `cc` as the payment type value, you may specify a custom value representation in your `validation` language file by defining a `values` array:
+Instead of displaying `cc` as the payment type value, you may specify a more user-friendly value representation in your `resources/lang/xx/validation.php` language file by defining a `values` array:
'values' => [
'payment_type' => [
@@ -585,7 +624,7 @@ Instead of displaying `cc` as the payment type value, you may specify a custom v
],
],
-Now if the validation rule fails it will produce the following message:
+After defining this value, the validation rule will produce the following error message:
The credit card number field is required when payment type is credit card.
@@ -680,7 +719,7 @@ Below is a list of all available validation rules and their function:
#### accepted
-The field under validation must be _yes_, _on_, _1_, or _true_. This is useful for validating "Terms of Service" acceptance.
+The field under validation must be `"yes"`, `"on"`, `1`, or `true`. This is useful for validating "Terms of Service" acceptance or similar fields.
#### active_url
@@ -690,7 +729,7 @@ The field under validation must have a valid A or AAAA record according to the `
#### after:_date_
-The field under validation must be a value after a given date. The dates will be passed into the `strtotime` PHP function:
+The field under validation must be a value after a given date. The dates will be passed into the `strtotime` PHP function in order to be converted to a valid `DateTime` instance:
'start_date' => 'required|date|after:tomorrow'
@@ -731,12 +770,12 @@ Stop running validation rules after the first validation failure.
#### before:_date_
-The field under validation must be a value preceding the given date. The dates will be passed into the PHP `strtotime` function. In addition, like the [`after`](#rule-after) rule, the name of another field under validation may be supplied as the value of `date`.
+The field under validation must be a value preceding the given date. The dates will be passed into the PHP `strtotime` function in order to be converted into a valid `DateTime` instance. In addition, like the [`after`](#rule-after) rule, the name of another field under validation may be supplied as the value of `date`.
#### before\_or\_equal:_date_
-The field under validation must be a value preceding or equal to the given date. The dates will be passed into the PHP `strtotime` function. In addition, like the [`after`](#rule-after) rule, the name of another field under validation may be supplied as the value of `date`.
+The field under validation must be a value preceding or equal to the given date. The dates will be passed into the PHP `strtotime` function in order to be converted into a valid `DateTime` instance. In addition, like the [`after`](#rule-after) rule, the name of another field under validation may be supplied as the value of `date`.
#### between:_min_,_max_
@@ -751,7 +790,7 @@ The field under validation must be able to be cast as a boolean. Accepted input
#### confirmed
-The field under validation must have a matching field of `foo_confirmation`. For example, if the field under validation is `password`, a matching `password_confirmation` field must be present in the input.
+The field under validation must have a matching field of `{field}_confirmation`. For example, if the field under validation is `password`, a matching `password_confirmation` field must be present in the input.
#### date
@@ -761,7 +800,7 @@ The field under validation must be a valid, non-relative date according to the `
#### date_equals:_date_
-The field under validation must be equal to the given date. The dates will be passed into the PHP `strtotime` function.
+The field under validation must be equal to the given date. The dates will be passed into the PHP `strtotime` function in order to be converted into a valid `DateTime` instance.
#### date_format:_format_
@@ -792,12 +831,13 @@ The file under validation must be an image meeting the dimension constraints as
Available constraints are: _min\_width_, _max\_width_, _min\_height_, _max\_height_, _width_, _height_, _ratio_.
-A _ratio_ constraint should be represented as width divided by height. This can be specified either by a statement like `3/2` or a float like `1.5`:
+A _ratio_ constraint should be represented as width divided by height. This can be specified either by a fraction like `3/2` or a float like `1.5`:
'avatar' => 'dimensions:ratio=3/2'
Since this rule requires several arguments, you may use the `Rule::dimensions` method to fluently construct the rule:
+ use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
Validator::make($data, [
@@ -813,15 +853,15 @@ Since this rule requires several arguments, you may use the `Rule::dimensions` m
When validating arrays, the field under validation must not have any duplicate values:
'foo.*.id' => 'distinct'
-
+
You may add `ignore_case` to the validation rule's arguments to make the rule ignore capitalization differences:
-
+
'foo.*.id' => 'distinct:ignore_case'
#### email
-The field under validation must be formatted as an e-mail address. Under the hood, this validation rule makes use of the [`egulias/email-validator`](https://github.com/egulias/EmailValidator) package for validating the email address. By default the `RFCValidation` validator is applied, but you can apply other validation styles as well:
+The field under validation must be formatted as an email address. This validation rule utilizes the [`egulias/email-validator`](https://github.com/egulias/EmailValidator) package for validating the email address. By default the `RFCValidation` validator is applied, but you can apply other validation styles as well:
'email' => 'email:rfc,dns'
@@ -835,7 +875,9 @@ The example above will apply the `RFCValidation` and `DNSCheckValidation` valida
- `filter`: `FilterEmailValidation`
-The `filter` validator, which uses PHP's `filter_var` function under the hood, ships with Laravel and is Laravel's pre-5.8 behavior. The `dns` and `spoof` validators require the PHP `intl` extension.
+The `filter` validator, which uses PHP's `filter_var` function, ships with Laravel and was Laravel's default email validation behavior prior to Laravel version 5.8.
+
+> {note} The `dns` and `spoof` validators require the PHP `intl` extension.
#### ends_with:_foo_,_bar_,...
@@ -855,21 +897,23 @@ The field under validation will be excluded from the request data returned by th
#### exists:_table_,_column_
-The field under validation must exist on a given database table.
+The field under validation must exist in a given database table.
#### Basic Usage Of Exists Rule
'state' => 'exists:states'
-If the `column` option is not specified, the field name will be used.
+If the `column` option is not specified, the field name will be used. So, in this case, the rule will validate that the `states` database table contains a record with a `state` column value matching the request's `state` attribute value.
#### Specifying A Custom Column Name
+You may explicitly specify the database column name that should be used by the validation rule by placing it after the database table name:
+
'state' => 'exists:states,abbreviation'
-Occasionally, you may need to specify a specific database connection to be used for the `exists` query. You can accomplish this by prepending the connection name to the table name using "dot" syntax:
+Occasionally, you may need to specify a specific database connection to be used for the `exists` query. You can accomplish this by prepending the connection name to the table name:
'email' => 'exists:connection.staff,email'
@@ -879,6 +923,7 @@ Instead of specifying the table name directly, you may specify the Eloquent mode
If you would like to customize the query executed by the validation rule, you may use the `Rule` class to fluently define the rule. In this example, we'll also specify the validation rules as an array instead of using the `|` character to delimit them:
+ use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
Validator::make($data, [
@@ -913,13 +958,14 @@ The field under validation must be greater than or equal to the given _field_. T
#### image
-The file under validation must be an image (jpg, jpeg, png, bmp, gif, svg, or webp)
+The file under validation must be an image (jpg, jpeg, png, bmp, gif, svg, or webp).
#### in:_foo_,_bar_,...
The field under validation must be included in the given list of values. Since this rule often requires you to `implode` an array, the `Rule::in` method may be used to fluently construct the rule:
+ use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
Validator::make($data, [
@@ -983,7 +1029,7 @@ The file under validation must match one of the given MIME types:
'video' => 'mimetypes:video/avi,video/mpeg,video/quicktime'
-To determine the MIME type of the uploaded file, the file's contents will be read and the framework will attempt to guess the MIME type, which may be different from the client provided MIME type.
+To determine the MIME type of the uploaded file, the file's contents will be read and the framework will attempt to guess the MIME type, which may be different from the client's provided MIME type.
#### mimes:_foo_,_bar_,...
@@ -995,9 +1041,9 @@ The file under validation must have a MIME type corresponding to one of the list
'photo' => 'mimes:jpg,bmp,png'
-Even though you only need to specify the extensions, this rule actually validates against the MIME type of the file by reading the file's contents and guessing its MIME type.
+Even though you only need to specify the extensions, this rule actually validates the MIME type of the file by reading the file's contents and guessing its MIME type. A full listing of MIME types and their corresponding extensions may be found at the following location:
-A full listing of MIME types and their corresponding extensions may be found at the following location: [https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types](https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types)
+[https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types](https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types)
#### min:_value_
@@ -1030,12 +1076,12 @@ The field under validation must not match the given regular expression.
Internally, this rule uses the PHP `preg_match` function. The pattern specified should obey the same formatting required by `preg_match` and thus also include valid delimiters. For example: `'email' => 'not_regex:/^.+$/i'`.
-**Note:** When using the `regex` / `not_regex` patterns, it may be necessary to specify rules in an array instead of using pipe delimiters, especially if the regular expression contains a pipe character.
+> {note} When using the `regex` / `not_regex` patterns, it may be necessary to specify your validation rules using an array instead of using `|` delimiters, especially if the regular expression contains a `|` character.
#### nullable
-The field under validation may be `null`. This is particularly useful when validating primitive such as strings and integers that can contain `null` values.
+The field under validation may be `null`.
#### numeric
@@ -1045,7 +1091,7 @@ The field under validation must be numeric.
#### password
-The field under validation must match the authenticated user's password. You may specify an authentication guard using the rule's first parameter:
+The field under validation must match the authenticated user's password. You may specify an [authentication guard](/docs/{{version}}/authentication) using the rule's first parameter:
'password' => 'password:api'
@@ -1061,7 +1107,7 @@ The field under validation must match the given regular expression.
Internally, this rule uses the PHP `preg_match` function. The pattern specified should obey the same formatting required by `preg_match` and thus also include valid delimiters. For example: `'email' => 'regex:/^.+@.+$/i'`.
-**Note:** When using the `regex` / `not_regex` patterns, it may be necessary to specify rules in an array instead of using pipe delimiters, especially if the regular expression contains a pipe character.
+> {note} When using the `regex` / `not_regex` patterns, it may be necessary to specify rules in an array instead of using `|` delimiters, especially if the regular expression contains a `|` character.
#### required
@@ -1082,8 +1128,9 @@ The field under validation must be present in the input data and not empty. A fi
The field under validation must be present and not empty if the _anotherfield_ field is equal to any _value_.
-If you would like to construct a more complex condition for the `required_if` rule, you may use the `Rule::requiredIf` method. This methods accepts a boolean or a Closure. When passed a Closure, the Closure should return `true` or `false` to indicate if the field under validation is required:
+If you would like to construct a more complex condition for the `required_if` rule, you may use the `Rule::requiredIf` method. This methods accepts a boolean or a closure. When passed a closure, the closure should return `true` or `false` to indicate if the field under validation is required:
+ use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
Validator::make($request->all(), [
@@ -1169,22 +1216,23 @@ Instead of specifying the table name directly, you may specify the Eloquent mode
'email' => 'unique:App\Models\User,email_address'
-The `column` option may be used to specify the field's corresponding database column. If the `column` option is not specified, the field name will be used.
+The `column` option may be used to specify the field's corresponding database column. If the `column` option is not specified, the name of the field under validation will be used.
'email' => 'unique:users,email_address'
-**Custom Database Connection**
+**Specifying A Custom Database Connection**
-Occasionally, you may need to set a custom connection for database queries made by the Validator. As seen above, setting `unique:users` as a validation rule will use the default database connection to query the database. To override this, specify the connection and the table name using "dot" syntax:
+Occasionally, you may need to set a custom connection for database queries made by the Validator. To accomplish this, you may prepend the connection name to the table name:
'email' => 'unique:connection.users,email_address'
**Forcing A Unique Rule To Ignore A Given ID:**
-Sometimes, you may wish to ignore a given ID during the unique check. For example, consider an "update profile" screen that includes the user's name, e-mail address, and location. You will probably want to verify that the e-mail address is unique. However, if the user only changes the name field and not the e-mail field, you do not want a validation error to be thrown because the user is already the owner of the e-mail address.
+Sometimes, you may wish to ignore a given ID during unique validation. For example, consider an "update profile" screen that includes the user's name, email address, and location. You will probably want to verify that the email address is unique. However, if the user only changes the name field and not the email field, you do not want a validation error to be thrown because the user is already the owner of the email address in question.
To instruct the validator to ignore the user's ID, we'll use the `Rule` class to fluently define the rule. In this example, we'll also specify the validation rules as an array instead of using the `|` character to delimit the rules:
+ use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
Validator::make($data, [
@@ -1196,7 +1244,7 @@ To instruct the validator to ignore the user's ID, we'll use the `Rule` class to
> {note} You should never pass any user controlled request input into the `ignore` method. Instead, you should only pass a system generated unique ID such as an auto-incrementing ID or UUID from an Eloquent model instance. Otherwise, your application will be vulnerable to an SQL injection attack.
-Instead of passing the model key's value to the `ignore` method, you may pass the entire model instance. Laravel will automatically extract the key from the model:
+Instead of passing the model key's value to the `ignore` method, you may also pass the entire model instance. Laravel will automatically extract the key from the model:
Rule::unique('users')->ignore($user)
@@ -1210,7 +1258,7 @@ By default, the `unique` rule will check the uniqueness of the column matching t
**Adding Additional Where Clauses:**
-You may also specify additional query constraints by customizing the query using the `where` method. For example, let's add a constraint that verifies the `account_id` is `1`:
+You may specify additional query conditions by customizing the query using the `where` method. For example, let's add a query condition that scopes the query to only search records that have an `account_id` column value of `1`:
'email' => Rule::unique('users')->where(function ($query) {
return $query->where('account_id', 1);
@@ -1234,7 +1282,9 @@ The field under validation must be a valid RFC 4122 (version 1, 3, 4, or 5) univ
You may occasionally wish to not validate a given field if another field has a given value. You may accomplish this using the `exclude_if` validation rule. In this example, the `appointment_date` and `doctor_name` fields will not be validated if the `has_appointment` field has a value of `false`:
- $v = Validator::make($data, [
+ use Illuminate\Support\Facades\Validator;
+
+ $validator = Validator::make($data, [
'has_appointment' => 'required|bool',
'appointment_date' => 'exclude_if:has_appointment,false|required|date',
'doctor_name' => 'exclude_if:has_appointment,false|required|string',
@@ -1242,7 +1292,7 @@ You may occasionally wish to not validate a given field if another field has a g
Alternatively, you may use the `exclude_unless` rule to not validate a given field unless another field has a given value:
- $v = Validator::make($data, [
+ $validator = Validator::make($data, [
'has_appointment' => 'required|bool',
'appointment_date' => 'exclude_unless:has_appointment,true|required|date',
'doctor_name' => 'exclude_unless:has_appointment,true|required|string',
@@ -1251,9 +1301,9 @@ Alternatively, you may use the `exclude_unless` rule to not validate a given fie
#### Validating When Present
-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:
+In some situations, you may wish to run validation checks against a field **only** if that field is present in the data being validated. To quickly accomplish this, add the `sometimes` rule to your rule list:
- $v = Validator::make($data, [
+ $v = Validator::make($request->all(), [
'email' => 'sometimes|required|email',
]);
@@ -1266,46 +1316,50 @@ In the example above, the `email` field will only be validated if it is present
Sometimes you may wish to add validation rules based on more complex conditional logic. For example, 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, [
+ use Illuminate\Support\Facades\Validator;
+
+ $validator = Validator::make($request->all(), [
'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 resale shop, or maybe they just enjoy collecting. To conditionally add this requirement, we can use the `sometimes` method on the `Validator` instance.
+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 resale shop, or maybe they just enjoy collecting games. 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;
});
-The first argument passed to the `sometimes` method is the name of the field we are conditionally validating. The second argument is a list of 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:
+The first argument passed to the `sometimes` method is the name of the field we are conditionally validating. The second argument is a list of 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;
});
-> {tip} The `$input` parameter passed to your `Closure` will be an instance of `Illuminate\Support\Fluent` and may be used to access your input and files.
+> {tip} The `$input` parameter passed to your closure will be an instance of `Illuminate\Support\Fluent` and may be used to access your input and files under validation.
## Validating Arrays
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:
+ use Illuminate\Support\Facades\Validator;
+
$validator = Validator::make($request->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:
+You may also validate each element of an array. For example, to validate that each email in a given array input field is unique, you may do the following:
$validator = Validator::make($request->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:
+Likewise, you may use the `*` character when specifying [custom validation messages in your language files](#custom-messages-for-specific-attributes), 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',
+ 'unique' => 'Each person must have a unique email address',
]
],
@@ -1315,7 +1369,7 @@ Likewise, you may use the `*` character when specifying your validation messages
### Using Rule Objects
-Laravel provides a variety of helpful validation rules; however, you may wish to specify some of your own. One method of registering custom validation rules is using rule objects. To generate a new rule object, you may use the `make:rule` Artisan command. Let's use this command to generate a rule that verifies a string is uppercase. Laravel will place the new rule in the `app/Rules` directory:
+Laravel provides a variety of helpful validation rules; however, you may wish to specify some of your own. One method of registering custom validation rules is using rule objects. To generate a new rule object, you may use the `make:rule` Artisan command. Let's use this command to generate a rule that verifies a string is uppercase. Laravel will place the new rule in the `app/Rules` directory. If this directory does not exist, Laravel will create it when you execute the Artisan command to create your rule:
php artisan make:rule Uppercase
@@ -1375,7 +1429,9 @@ Once the rule has been defined, you may attach it to a validator by passing an i
### Using Closures
-If you only need the functionality of a custom rule once throughout your application, you may use a Closure instead of a rule object. The Closure receives the attribute's name, the attribute's value, and a `$fail` callback that should be called if validation fails:
+If you only need the functionality of a custom rule once throughout your application, you may use a closure instead of a rule object. The closure receives the attribute's name, the attribute's value, and a `$fail` callback that should be called if validation fails:
+
+ use Illuminate\Support\Facades\Validator;
$validator = Validator::make($request->all(), [
'title' => [
@@ -1383,102 +1439,25 @@ If you only need the functionality of a custom rule once throughout your applica
'max:255',
function ($attribute, $value, $fail) {
if ($value === 'foo') {
- $fail($attribute.' is invalid.');
+ $fail('The '.$attribute.' is invalid.');
}
},
],
]);
-
-### Using Extensions
-
-Another method of registering custom validation rules is using the `extend` method on the `Validator` [facade](/docs/{{version}}/facades). Let's use this method within a [service provider](/docs/{{version}}/providers) to register a custom validation rule:
-
-
+### Implicit Rules
- namespace App\Providers;
+By default, when an attribute being validated is not present or contains an empty string, normal validation rules, including custom rules, are not run. For example, the [`unique`](#rule-unique) rule will not be run against an empty string:
- use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
- class AppServiceProvider extends ServiceProvider
- {
- /**
- * Register any application services.
- *
- * @return void
- */
- public function register()
- {
- //
- }
-
- /**
- * Bootstrap any application services.
- *
- * @return void
- */
- public function boot()
- {
- Validator::extend('foo', function ($attribute, $value, $parameters, $validator) {
- return $value == 'foo';
- });
- }
- }
-
-The custom validator Closure receives four arguments: the name of the `$attribute` being validated, the `$value` of the attribute, an array of `$parameters` passed to the rule, and the `Validator` instance.
-
-You may also pass a class and method to the `extend` method instead of a Closure:
-
- Validator::extend('foo', 'FooValidator@validate');
-
-
-#### Defining The Error Message
-
-You will also need to define an error message for your custom rule. You can do so either using an inline custom message array or by adding an entry in the validation language file. This message should be placed in the first level of the array, not within the `custom` array, which is only for attribute-specific error messages:
-
- "foo" => "Your input was invalid!",
-
- "accepted" => "The :attribute must be accepted.",
-
- // The rest of the validation error messages...
-
-When creating a custom validation rule, you may sometimes need to define custom placeholder replacements for error messages. You may do so by creating a custom Validator as described above then making a call to the `replacer` method on the `Validator` facade. You may do this within the `boot` method of a [service provider](/docs/{{version}}/providers):
-
- /**
- * Bootstrap any application services.
- *
- * @return void
- */
- public function boot()
- {
- Validator::extend(...);
-
- Validator::replacer('foo', function ($message, $attribute, $rule, $parameters) {
- return str_replace(...);
- });
- }
-
-
-### Implicit Extensions
-
-By default, when an attribute being validated is not present or contains an empty string, normal validation rules, including custom extensions, are not run. For example, the [`unique`](#rule-unique) rule will not be run against an empty string:
-
$rules = ['name' => 'unique:users,name'];
$input = ['name' => ''];
Validator::make($input, $rules)->passes(); // true
-For a rule to run even when an attribute is empty, the rule must imply that the attribute is required. To create such an "implicit" extension, use the `Validator::extendImplicit()` method:
-
- Validator::extendImplicit('foo', function ($attribute, $value, $parameters, $validator) {
- return $value == 'foo';
- });
-
-> {note} An "implicit" extension only _implies_ that the attribute is required. Whether it actually invalidates a missing or empty attribute is up to you.
-
-
-#### Implicit Rule Objects
+For a custom rule to run even when an attribute is empty, the rule must imply that the attribute is required. To create an "implicit" rule, implement the `Illuminate\Contracts\Validation\ImplicitRule` interface. This interface serves as a "marker interface" for the validator; therefore, it does not contain any additional methods you need to implement beyond the methods required by the typical `Rule` interface.
-If you would like a rule object to run when an attribute is empty, you should implement the `Illuminate\Contracts\Validation\ImplicitRule` interface. This interface serves as a "marker interface" for the validator; therefore, it does not contain any methods you need to implement.
+> {note} An "implicit" rule only _implies_ that the attribute is required. Whether it actually invalidates a missing or empty attribute is up to you.
diff --git a/verification.md b/verification.md
index 1f91d2ff59a..a44adb15d62 100644
--- a/verification.md
+++ b/verification.md
@@ -13,14 +13,14 @@
## Introduction
-Many web applications require users to verify their email addresses before using the application. Rather than forcing you to re-implement this on each application, Laravel provides convenient methods for sending and verifying email verification requests.
+Many web applications require users to verify their email addresses before using the application. Rather than forcing you to re-implement this feature by hand for each application you create, Laravel provides convenient built-in services for sending and verifying email verification requests.
-> {tip} Want to get started fast? Install [Laravel Jetstream](https://jetstream.laravel.com) in a fresh Laravel application. After migrating your database, navigate your browser to `/register` or any other URL that is assigned to your application. Jetstream will take care of scaffolding your entire authentication system, including email verification support!
+> {tip} Want to get started fast? Install one of the [Laravel application starter kits](/docs/{{version}}/starter-kits) in a fresh Laravel application. The starter kits will take care of scaffolding your entire authentication system, including email verification support.
### Model Preparation
-To get started, verify that your `App\Models\User` model implements the `Illuminate\Contracts\Auth\MustVerifyEmail` contract:
+Before getting started, verify that your `App\Models\User` model implements the `Illuminate\Contracts\Auth\MustVerifyEmail` contract:
### Database Preparation
-Next, your `user` table must contain an `email_verified_at` column to store the date and time that the email address was verified. By default, the `users` table migration included with the Laravel framework already includes this column. So, all you need to do is run your database migrations:
+Next, your `user` table must contain an `email_verified_at` column to store the date and time that the user's email address was verified. By default, the `users` table migration included with the Laravel framework already includes this column. So, all you need to do is run your database migrations:
php artisan migrate
## Routing
-To properly implement email verification, three routes will need to be defined. First, a route will be needed to display a notice to the user that they should click the email verification link in the verification email that Laravel sent them after registration. Second, a route will be needed to handle requests generated when the user clicks the email verification link in the email. Third, a route will be needed to resend a verification link if the user accidentally loses the first one.
+To properly implement email verification, three routes will need to be defined. First, a route will be needed to display a notice to the user that they should click the email verification link in the verification email that Laravel sent them after registration.
+
+Second, a route will be needed to handle requests generated when the user clicks the email verification link in the email.
+
+Third, a route will be needed to resend a verification link if the user accidentally loses the first verification link.
### The Email Verification Notice
-As mentioned previously, a route should be defined that will return a view instructing the user to click the email verification link that was emailed to them by Laravel. This view will be displayed to users when they try to access other parts of the application without verifying their email address first. Remember, the link is automatically emailed to the user as long as your `App\Models\User` model implements the `MustVerifyEmail` interface:
+As mentioned previously, a route should be defined that will return a view instructing the user to click the email verification link that was emailed to them by Laravel after registration. This view will be displayed to users when they try to access other parts of the application without verifying their email address first. Remember, the link is automatically emailed to the user as long as your `App\Models\User` model implements the `MustVerifyEmail` interface:
Route::get('/email/verify', function () {
return view('auth.verify-email');
- })->middleware(['auth'])->name('verification.notice');
+ })->middleware('auth')->name('verification.notice');
The route that returns the email verification notice should be named `verification.notice`. It is important that the route be assigned this exact name since the `verified` middleware [included with Laravel](#protecting-routes) will automatically redirect to this route name if a user has not verified their email address.
-> {tip} When manually implementing email verification, you are required to define the contents of the verification notice view yourself. If you would like scaffolding that includes all necessary authentication and verification views, check out [Laravel Jetstream](https://jetstream.laravel.com).
+> {tip} When manually implementing email verification, you are required to define the contents of the verification notice view yourself. If you would like scaffolding that includes all necessary authentication and verification views, check out the [Laravel application starter kits](/docs/{{version}}/starter-kits).
### The Email Verification Handler
-Next, we need a route that will handle requests generated when the user clicks the email verification link that was emailed to them. This route should be named `verification.verify` and be assigned the `auth` and `signed` middlewares:
+Next, we need to define a route that will handle requests generated when the user clicks the email verification link that was emailed to them. This route should be named `verification.verify` and be assigned the `auth` and `signed` middlewares:
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Illuminate\Http\Request;
@@ -84,7 +88,7 @@ Next, we need a route that will handle requests generated when the user clicks t
return redirect('/home');
})->middleware(['auth', 'signed'])->name('verification.verify');
-Before moving on, let's take a closer look at this route. First, you'll notice we are using an `EmailVerificationRequest` request type instead of the typical `Illuminate\Http\Request` instance. The `EmailVerificationRequest` is a [form request](/docs/{{version}}/validation#form-request-validation) that is included with Laravel. This request will take care of automatically validating the request's `id` and `hash` parameters.
+Before moving on, let's take a closer look at this route. First, you'll notice we are using an `EmailVerificationRequest` request type instead of the typical `Illuminate\Http\Request` instance. The `EmailVerificationRequest` is a [form request](/docs/{{version}}/validation#form-request-validation) that is included with Laravel. This request will automatically take care of validating the request's `id` and `hash` parameters.
Next, we can proceed directly to calling the `fulfill` method on the request. This method will call the `markEmailAsVerified` method on the authenticated user and dispatch the `Illuminate\Auth\Events\Verified` event. The `markEmailAsVerified` method is available to the default `App\Models\User` model via the `Illuminate\Foundation\Auth\User` base class. Once the user's email address has been verified, you may redirect them wherever you wish.
@@ -98,16 +102,16 @@ Sometimes a user may misplace or accidentally delete the email address verificat
Route::post('/email/verification-notification', function (Request $request) {
$request->user()->sendEmailVerificationNotification();
- return back()->with('status', 'verification-link-sent');
+ return back()->with('message', 'Verification link sent!');
})->middleware(['auth', 'throttle:6,1'])->name('verification.send');
### Protecting Routes
-[Route middleware](/docs/{{version}}/middleware) can be used to only allow verified users to access a given route. Laravel ships with a `verified` middleware, which references the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` class. Since this middleware is already registered in your application's HTTP kernel, all you need to do is attach the middleware to a route definition:
+[Route middleware](/docs/{{version}}/middleware) may be used to only allow verified users to access a given route. Laravel ships with a `verified` middleware, which references the `Illuminate\Auth\Middleware\EnsureEmailIsVerified` class. Since this middleware is already registered in your application's HTTP kernel, all you need to do is attach the middleware to a route definition:
- Route::get('profile', function () {
- // Only verified users may enter...
+ Route::get('/profile', function () {
+ // Only verified users may access this route...
})->middleware('verified');
If an unverified user attempts to access a route that has been assigned this middleware, they will automatically be redirected to the `verification.notice` [named route](/docs/{{version}}/routing#named-routes).
@@ -115,7 +119,7 @@ If an unverified user attempts to access a route that has been assigned this mid
## Events
-When using [Laravel Jetstream](https://jetstream.laravel.com), Laravel dispatches [events](/docs/{{version}}/events) during the email verification process. If you are manually handling email verification for your application, you may wish to manually dispatch these events after verification is completed. You may attach listeners to these events in your `EventServiceProvider`:
+When using the [Laravel application starter kits](/docs/{{version}}/starter-kits), Laravel dispatches [events](/docs/{{version}}/events) during the email verification process. If you are manually handling email verification for your application, you may wish to manually dispatch these events after verification is completed. You may attach listeners to these events in your application's `EventServiceProvider`:
/**
* The event listener mappings for the application.
diff --git a/views.md b/views.md
index b2529f675a1..9ee9c0abd7f 100644
--- a/views.md
+++ b/views.md
@@ -1,25 +1,30 @@
# Views
-- [Creating Views](#creating-views)
+- [Introduction](#introduction)
+- [Creating & Rendering Views](#creating-and-rendering-views)
+ - [Nested View Directories](#nested-view-directories)
+ - [Creating The First Available View](#creating-the-first-available-view)
+ - [Determining If A View Exists](#determining-if-a-view-exists)
- [Passing Data To Views](#passing-data-to-views)
- [Sharing Data With All Views](#sharing-data-with-all-views)
- [View Composers](#view-composers)
+ - [View Creators](#view-creators)
- [Optimizing Views](#optimizing-views)
-
-## Creating Views
+
+## Introduction
-> {tip} Looking for more information on how to write Blade templates? Check out the full [Blade documentation](/docs/{{version}}/blade) to get started.
-
-Views contain the HTML served by your application and separate your controller / application logic from your presentation logic. Views are stored in the `resources/views` directory. A simple view might look something like this:
+Of course, it's not practical to return entire HTML documents strings directly from your routes and controllers. Thankfully, views provide a convenient way to place all of our HTML in separate files. Views separate your controller / application logic from your presentation logic and are stored in the `resources/views` directory. A simple view might look something like this:
-
+```html
+
-
-
-
Hello, {{ $name }}
-
-
+
+
+
Hello, {{ $name }}
+
+
+```
Since this view is stored at `resources/views/greeting.blade.php`, we may return it using the global `view` helper like so:
@@ -27,53 +32,75 @@ Since this view is stored at `resources/views/greeting.blade.php`, we may return
return view('greeting', ['name' => 'James']);
});
-As you can see, the first argument passed to the `view` helper corresponds to the name of the view file in the `resources/views` directory. The second argument is an array of data that should be made available to the view. In this case, we are passing the `name` variable, which is displayed in the view using [Blade syntax](/docs/{{version}}/blade).
+> {tip} Looking for more information on how to write Blade templates? Check out the full [Blade documentation](/docs/{{version}}/blade) to get started.
-Views may also be nested within subdirectories of the `resources/views` directory. "Dot" notation may be used to reference nested views. For example, if your view is stored at `resources/views/admin/profile.blade.php`, you may reference it like so:
+
+## Creating & Rendering Views
- return view('admin.profile', $data);
+You may create a view by placing a file with the `.blade.php` extension in your application's `resources/views` directory. The `.blade.php` extension informs the framework that the file contains a [Blade template](/docs/{{version}}/blade). Blade templates contain HTML as well as Blade directives that allow you to easily echo values, create "if" statements, iterate over data, and more.
-> {note} View directory names should not contain the `.` character.
+Once you have created a view, you may return it from one of your application's routes or controllers using the global `view` helper:
-
-#### Determining If A View Exists
+ Route::get('/', function () {
+ return view('greeting', ['name' => 'James']);
+ });
-If you need to determine if a view exists, you may use the `View` facade. The `exists` method will return `true` if the view exists:
+Views may also be returned using the `View` facade:
use Illuminate\Support\Facades\View;
- if (View::exists('emails.customer')) {
- //
- }
+ return View::make('greeting', ['name' => 'James']);
-
-#### Creating The First Available View
+As you can see, the first argument passed to the `view` helper corresponds to the name of the view file in the `resources/views` directory. The second argument is an array of data that should be made available to the view. In this case, we are passing the `name` variable, which is displayed in the view using [Blade syntax](/docs/{{version}}/blade).
+
+
+### Nested View Directories
+
+Views may also be nested within subdirectories of the `resources/views` directory. "Dot" notation may be used to reference nested views. For example, if your view is stored at `resources/views/admin/profile.blade.php`, you may return it from one of your application's routes / controllers like so:
+
+ return view('admin.profile', $data);
-Using the `first` method, you may create the first view that exists in a given array of views. This is useful if your application or package allows views to be customized or overwritten:
+> {note} View directory names should not contain the `.` character.
- return view()->first(['custom.admin', 'admin'], $data);
+
+### Creating The First Available View
-You may also call this method via the `View` [facade](/docs/{{version}}/facades):
+Using the `View` facade's `first` method, you may create the first view that exists in a given array of views. This may be useful if your application or package allows views to be customized or overwritten:
use Illuminate\Support\Facades\View;
return View::first(['custom.admin', 'admin'], $data);
+
+### Determining If A View Exists
+
+If you need to determine if a view exists, you may use the `View` facade. The `exists` method will return `true` if the view exists:
+
+ use Illuminate\Support\Facades\View;
+
+ if (View::exists('emails.customer')) {
+ //
+ }
+
## Passing Data To Views
-As you saw in the previous examples, you may pass an array of data to views:
+As you saw in the previous examples, you may pass an array of data to views to make that data available to the view:
return view('greetings', ['name' => 'Victoria']);
-When passing information in this manner, the data should be an array with key / value pairs. Inside your view, you can then access each value using its corresponding key, such as ``. As an alternative to passing a complete array of data to the `view` helper function, you may use the `with` method to add individual pieces of data to the view:
+When passing information in this manner, the data should be an array with key / value pairs. After providing data to a view, you can then access each value within your view using the data's keys, such as ``.
- return view('greeting')->with('name', 'Victoria');
+As an alternative to passing a complete array of data to the `view` helper function, you may use the `with` method to add individual pieces of data to the view. The `with` method returns an instance of the view object so that you can continue chaining methods before returning the view:
+
+ return view('greeting')
+ ->with('name', 'Victoria')
+ ->with('occupation', 'Astronaut');
-#### Sharing Data With All Views
+### Sharing Data With All Views
-Occasionally, you may need to share a piece of data with all views that are rendered by your application. You may do so using the view facade's `share` method. Typically, you should place calls to `share` within a service provider's `boot` method. You are free to add them to the `AppServiceProvider` or generate a separate service provider to house them:
+Occasionally, you may need to share data with all views that are rendered by your application. You may do so using the `View` facade's `share` method. Typically, you should place calls to the `share` method within a service provider's `boot` method. You are free to add them to the `App\Providers\AppServiceProvider` class or generate a separate service provider to house them:
## View Composers
-View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want to be bound to a view each time that view is rendered, a view composer can help you organize that logic into a single location.
+View composers are callbacks or class methods that are called when a view is rendered. If you have data that you want to be bound to a view each time that view is rendered, a view composer can help you organize that logic into a single location. View composers may prove particularly useful if the same view is returned by multiple routes or controllers within your application and always needs a particular piece of data.
+
+Typically, view composers will be registered within one of your application's [service providers](/docs/{{version}}/providers). In this example, we'll assume that we have created a new `App\Providers\ViewServiceProvider` to house this logic.
-For this example, let's register the view composers within a [service provider](/docs/{{version}}/providers). We'll use the `View` facade to access the underlying `Illuminate\Contracts\View\Factory` contract implementation. Remember, Laravel does not include a default directory for view composers. You are free to organize them however you wish. For example, you could create an `app/Http/View/Composers` directory:
+We'll use the `View` facade's `composer` method to register the view composer. Laravel does not include a default directory for class based view composers, so you are free to organize them however you wish. For example, you could create an `app/Http/View/Composers` directory to house all of your application's view composers:
{note} Remember, if you create a new service provider to contain your view composer registrations, you will need to add the service provider to the `providers` array in the `config/app.php` configuration file.
-Now that we have registered the composer, the `ProfileComposer@compose` method will be executed each time the `profile` view is being rendered. So, let's define the composer class:
+Now that we have registered the composer, the `compose` method of the `App\Http\View\Composers\ProfileComposer` class will be executed each time the `profile` view is being rendered. Let's take a look at an example of the composer class:
{tip} All view composers are resolved via the [service container](/docs/{{version}}/container), so you may type-hint any dependencies you need within a composer's constructor.
+As you can see, all view composers are resolved via the [service container](/docs/{{version}}/container), so you may type-hint any dependencies you need within a composer's constructor.
#### Attaching A Composer To Multiple Views
You may attach a view composer to multiple views at once by passing an array of views as the first argument to the `composer` method:
+ use App\Http\Views\Composers\MultiComposer;
+
View::composer(
['profile', 'dashboard'],
- 'App\Http\View\Composers\MyViewComposer'
+ MultiComposer::class
);
The `composer` method also accepts the `*` character as a wildcard, allowing you to attach a composer to all views:
@@ -214,18 +242,21 @@ The `composer` method also accepts the `*` character as a wildcard, allowing you
});
-#### View Creators
+### View Creators
-View **creators** are very similar to view composers; however, they are executed immediately after the view is instantiated instead of waiting until the view is about to render. To register a view creator, use the `creator` method:
+View "creators" are very similar to view composers; however, they are executed immediately after the view is instantiated instead of waiting until the view is about to render. To register a view creator, use the `creator` method:
+
+ use App\Http\View\Creators\ProfileCreator;
+ use Illuminate\Support\Facades\View;
- View::creator('profile', 'App\Http\View\Creators\ProfileCreator');
+ View::creator('profile', ProfileCreator::class);
## Optimizing Views
-By default, views are compiled on demand. When a request is executed that renders a view, Laravel will determine if a compiled version of the view exists. If the file exists, Laravel will then determine if the uncompiled view has been modified more recently than the compiled view. If the compiled view either does not exist, or the uncompiled view has been modified, Laravel will recompile the view.
+By default, Blade template views are compiled on demand. When a request is executed that renders a view, Laravel will determine if a compiled version of the view exists. If the file exists, Laravel will then determine if the uncompiled view has been modified more recently than the compiled view. If the compiled view either does not exist, or the uncompiled view has been modified, Laravel will recompile the view.
-Compiling views during the request negatively impacts performance, so Laravel provides the `view:cache` Artisan command to precompile all of the views utilized by your application. For increased performance, you may wish to run this command as part of your deployment process:
+Compiling views during the request may have a small negative impact on performance, so Laravel provides the `view:cache` Artisan command to precompile all of the views utilized by your application. For increased performance, you may wish to run this command as part of your deployment process:
php artisan view:cache