diff --git a/artisan.md b/artisan.md
index 794326a4d6a..4667d222f3d 100644
--- a/artisan.md
+++ b/artisan.md
@@ -111,49 +111,55 @@ In addition to the commands provided with Artisan, you may build your own custom
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:
-```shell
+```shell tab=Creation
php artisan make:command SendEmails
```
-
-### Command Structure
-
-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.
+```php tab=Definition filename=app/Console/Commands/SendEmails.php
+send(User::find($this->argument('user')));
+ }
+}
+```
- /**
- * The console command description.
- *
- * @var string
- */
- protected $description = 'Send a marketing email to a user';
+```shell tab=Usage
+php artisan mail:send 1
+```
- /**
- * Execute the console command.
- */
- public function handle(DripEmailer $drip): void
- {
- $drip->send(User::find($this->argument('user')));
- }
- }
+
+### Command Structure
+
+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 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.
> [!NOTE]
> 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.
@@ -300,37 +306,41 @@ You may also make arguments optional or define default values for arguments:
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 = '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 tab=Definition filename=app/Console/Commands/SendEmails.php
+/**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+protected $signature = 'mail:send {user} {--queue}';
+```
-```shell
+```shell tab=Usage
php artisan mail:send 1 --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`:
+
#### 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, you should suffix the option name with a `=` sign:
- /**
- * The name and signature of the console command.
- *
- * @var string
- */
- protected $signature = 'mail:send {user} {--queue=}';
-
-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 tab=Definition filename=app/Console/Commands/SendEmails.php
+/**
+ * The name and signature of the console command.
+ *
+ * @var string
+ */
+protected $signature = 'mail:send {user} {--queue=}';
+```
-```shell
+```shell tab=Usage
php artisan mail:send 1 --queue=default
```
+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`.
+
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:
'mail:send {user} {--queue=default}'
diff --git a/blade.md b/blade.md
index e124c9cfb2a..1a6a4531dfb 100644
--- a/blade.md
+++ b/blade.md
@@ -761,7 +761,7 @@ If you would like to conditionally render your component, you may define a `shou
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:
-```blade
+```blade tab=Usage
```
@@ -1855,22 +1855,22 @@ As you can see, we will chain the `format` method onto whatever expression is pa
If you attempt to "echo" an object using Blade, the object's `__toString` method will be invoked. The [`__toString`](https://www.php.net/manual/en/language.oop5.magic.php#object.tostring) method is one of PHP's built-in "magic methods". However, sometimes you may not have control over the `__toString` method of a given class, such as when the class that you are interacting with belongs to a third-party library.
-In these cases, Blade allows you to register a custom echo handler for that particular type of object. To accomplish this, you should invoke Blade's `stringable` method. The `stringable` method accepts a closure. This closure should type-hint the type of object that it is responsible for rendering. Typically, the `stringable` method should be invoked within the `boot` method of your application's `AppServiceProvider` class:
-
- use Illuminate\Support\Facades\Blade;
- use Money\Money;
+In these cases, Blade allows you to register a custom echo handler for that particular type of object. To accomplish this, you should invoke Blade's `stringable` method. The `stringable` method accepts a closure. This closure should type-hint the type of object that it is responsible for rendering. Typically, the `stringable` method should be invoked within the `boot` method of your application's `AppServiceProvider` class. Once your custom echo handler has been defined, you may simply echo the object in your Blade template:
- /**
- * Bootstrap any application services.
- */
- public function boot(): void
- {
- Blade::stringable(function (Money $money) {
- return $money->formatTo('en_GB');
- });
- }
+```php tab=Definition filename=app/Providers/AppServiceProvider.php
+use Illuminate\Support\Facades\Blade;
+use Money\Money;
-Once your custom echo handler has been defined, you may simply echo the object in your Blade template:
+/**
+ * Bootstrap any application services.
+ */
+public function boot(): void
+{
+ Blade::stringable(function (Money $money) {
+ return $money->formatTo('en_GB');
+ });
+}
+```
```blade
Cost: {{ $money }}
@@ -1879,23 +1879,23 @@ Cost: {{ $money }}
### 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 configured default "disk" for the application. 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`. Once the custom conditional has been defined, you can use it within your templates:
- use Illuminate\Support\Facades\Blade;
-
- /**
- * Bootstrap any application services.
- */
- public function boot(): void
- {
- Blade::if('disk', function (string $value) {
- return config('filesystems.default') === $value;
- });
- }
+```php tab=Definition filename=app/Providers/AppServiceProvider.php
+use Illuminate\Support\Facades\Blade;
-Once the custom conditional has been defined, you can use it within your templates:
+/**
+ * Bootstrap any application services.
+ */
+public function boot(): void
+{
+ Blade::if('disk', function (string $value) {
+ return config('filesystems.default') === $value;
+ });
+}
+```
-```blade
+```blade tab=Usage
@disk('local')
@elsedisk('s3')
diff --git a/broadcasting.md b/broadcasting.md
index 7c42269e48a..d3e52d7d617 100644
--- a/broadcasting.md
+++ b/broadcasting.md
@@ -108,15 +108,15 @@ You can find detailed Reverb installation and usage instructions in the [Reverb
### Pusher Channels
-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:
+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.
-```shell
+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, you should configure your Pusher Channels credentials in your application's `.env` file.
+
+```shell tab=Installation
composer require pusher/pusher-php-server
```
-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, you should configure your Pusher Channels credentials in your application's `.env` file:
-
-```ini
+```ini tab=Configuration filename=.env
PUSHER_APP_ID="your-pusher-app-id"
PUSHER_APP_KEY="your-pusher-key"
PUSHER_APP_SECRET="your-pusher-secret"
@@ -646,42 +646,46 @@ 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.
-```shell
+1. To generate a channel class, use the `make:channel` Artisan command. This command will place a new channel class in the `App/Broadcasting` directory.
+2. Next, register your channel in your `routes/channels.php` file.
+3. 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:
+
+```shell tab=Creation
php artisan make:channel OrderChannel
```
-Next, register your channel in your `routes/channels.php` file:
-
- use App\Broadcasting\OrderChannel;
+```php tab=Registration filename=routes/channels.php
+use App\Broadcasting\OrderChannel;
- Broadcast::channel('orders.{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:
+```php tab=Usage filename=app/Broadcasting/OrderChannel.php
+id === $order->user_id;
- }
+ return $user->id === $order->user_id;
}
+}
+```
> [!NOTE]
> Like many other classes in Laravel, channel classes will automatically be resolved by the [service container](/docs/{{version}}/container). So, you may type-hint any dependencies required by your channel in its constructor.
diff --git a/cache.md b/cache.md
index e80e3a0a0f7..a8323683f7e 100644
--- a/cache.md
+++ b/cache.md
@@ -92,15 +92,13 @@ This table should also have a string partition key with a name that corresponds
Typically, DynamoDB will not proactively remove expired items from a table. Therefore, you should [enable Time to Live (TTL)](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TTL.html) on the table. When configuring the table's TTL settings, you should set the TTL attribute name to `expires_at`.
-Next, install the AWS SDK so that your Laravel application can communicate with DynamoDB:
+Next, install the AWS SDK so that your Laravel application can communicate with DynamoDB. In addition, you should ensure that values are provided for the DynamoDB cache store configuration options. Typically these options, such as `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, should be defined in your application's `.env` configuration file:
-```shell
+```shell tab=Installation
composer require aws/aws-sdk-php
```
-In addition, you should ensure that values are provided for the DynamoDB cache store configuration options. Typically these options, such as `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`, should be defined in your application's `.env` configuration file:
-
-```php
+```php tab=Configuration filename=config/cache.php
'dynamodb' => [
'driver' => 'dynamodb',
'key' => env('AWS_ACCESS_KEY_ID'),
diff --git a/controllers.md b/controllers.md
index c2c22c7bffa..b3789572ab5 100644
--- a/controllers.md
+++ b/controllers.md
@@ -27,39 +27,41 @@ Instead of defining all of your request handling logic as closures in your route
### Basic Controllers
-To quickly generate a new controller, you may run the `make:controller` Artisan command. By default, all of the controllers for your application are stored in the `app/Http/Controllers` directory:
+1. To quickly generate a new controller, you may run the `make:controller` Artisan command. By default, all of the controllers for your application are stored in the `app/Http/Controllers` directory
+2. Let's take a look at an example of a basic controller. A controller may have any number of public methods which will respond to incoming HTTP requests
+3. Once you have written a controller class and method, you may define a route to the controller method like so:
-```shell
+```shell tab=Creation
php artisan make:controller UserController
```
-Let's take a look at an example of a basic controller. A controller may have any number of public methods which will respond to incoming HTTP requests:
+```php tab=Definition filename=app/Http/Controllers/UserController.php
+ User::findOrFail($id)
- ]);
- }
+ return view('user.profile', [
+ 'user' => User::findOrFail($id)
+ ]);
}
+}
+```
-Once you have written a controller class and method, you may define a route to the controller method like so:
-
- use App\Http\Controllers\UserController;
+```php tab=Usage filename=routes/web.php
+use App\Http\Controllers\UserController;
- Route::get('/user/{id}', [UserController::class, 'show']);
+Route::get('/user/{id}', [UserController::class, 'show']);
+```
When an incoming request matches the specified route URI, the `show` method on the `App\Http\Controllers\UserController` class will be invoked and the route parameters will be passed to the method.
@@ -69,33 +71,37 @@ When an incoming request matches the specified route URI, the `show` method on t
### Single Action Controllers
-If a controller action is particularly complex, you might find it convenient to dedicate an entire controller class to that single action. To accomplish this, you may define a single `__invoke` method within the controller:
+If a controller action is particularly complex, you might find it convenient to dedicate an entire controller class to that single action. To accomplish this, you may define a single `__invoke` method within the controller.
- [!NOTE]
@@ -157,17 +163,20 @@ You may also define controller middleware as closures, which provides a convenie
If you think of each Eloquent model in your application as a "resource", it is typical to perform the same sets of actions against each resource in your application. For example, imagine your application contains a `Photo` model and a `Movie` model. It is likely that users can create, read, update, or delete these resources.
-Because of this common use case, Laravel resource routing assigns the typical create, read, update, and delete ("CRUD") routes to a controller with a single line of code. To get started, we can use the `make:controller` Artisan command's `--resource` option to quickly create a controller to handle these actions:
+Because of this common use case, Laravel resource routing assigns the typical create, read, update, and delete ("CRUD") routes to a controller with a single line of code.
-```shell
+1. To get started, we can use the `make:controller` Artisan command's `--resource` option to quickly create a controller to handle these actions.
+2. This command will generate a controller at `app/Http/Controllers/PhotoController.php`. The controller will contain a method for each of the available resource operations. Next, you may register a resource route that points to the controller.
+
+```shell tab=Creation
php artisan make:controller PhotoController --resource
```
-This command will generate a controller at `app/Http/Controllers/PhotoController.php`. The controller will contain a method for each of the available resource operations. Next, you may register a resource route that points to the controller:
-
- use App\Http\Controllers\PhotoController;
+```php tab=Usage filename=routes/web.php
+use App\Http\Controllers\PhotoController;
- Route::resource('photos', PhotoController::class);
+Route::resource('photos', PhotoController::class);
+```
This single route declaration creates multiple routes to handle a variety of actions on the resource. The generated controller will already have methods stubbed for each of these actions. Remember, you can always get a quick overview of your application's routes by running the `route:list` Artisan command.
diff --git a/eloquent-mutators.md b/eloquent-mutators.md
index 34a5ba2e7a6..5a757b78ffb 100644
--- a/eloquent-mutators.md
+++ b/eloquent-mutators.md
@@ -531,128 +531,136 @@ The `last_posted_at` attribute on the results of this query will be a simple str
## Custom Casts
-Laravel has a variety of built-in, helpful cast types; however, you may occasionally need to define your own cast types. To create a cast, execute the `make:cast` Artisan command. The new cast class will be placed in your `app/Casts` directory:
+Laravel has a variety of built-in, helpful cast types; however, you may occasionally need to define your own cast types. To create a cast, execute the `make:cast` Artisan command. The new cast class will be placed in your `app/Casts` directory.
-```shell
+All custom cast classes implement the `CastsAttributes` interface. Classes that implement this interface must define a `get` and `set` method. The `get` method is responsible for transforming a raw value from the database into a cast value, while the `set` method should transform a cast value into a raw value that can be stored in the database. As an example, we will re-implement the built-in `json` cast type as a custom cast type.
+
+Once you have defined a custom cast type, you may attach it to a model attribute using its class name.
+
+```shell tab=Creation
php artisan make:cast Json
```
-All custom cast classes implement the `CastsAttributes` interface. Classes that implement this interface must define a `get` and `set` method. The `get` method is responsible for transforming a raw value from the database into a cast value, while the `set` method should transform a cast value into a raw value that can be stored in the database. As an example, we will re-implement the built-in `json` cast type as a custom cast type:
-
- $attributes
+ * @return array
+ */
+ public function get(Model $model, string $key, mixed $value, array $attributes): array
{
- /**
- * Cast the given value.
- *
- * @param array $attributes
- * @return array
- */
- public function get(Model $model, string $key, mixed $value, array $attributes): array
- {
- return json_decode($value, true);
- }
-
- /**
- * Prepare the given value for storage.
- *
- * @param array $attributes
- */
- public function set(Model $model, string $key, mixed $value, array $attributes): string
- {
- return json_encode($value);
- }
+ return json_decode($value, true);
}
-Once you have defined a custom cast type, you may attach it to a model attribute using its class name:
+ /**
+ * Prepare the given value for storage.
+ *
+ * @param array $attributes
+ */
+ public function set(Model $model, string $key, mixed $value, array $attributes): string
+ {
+ return json_encode($value);
+ }
+}
+```
-
+ */
+ protected function casts(): array
{
- /**
- * Get the attributes that should be cast.
- *
- * @return array
- */
- protected function casts(): array
- {
- return [
- 'options' => Json::class,
- ];
- }
+ return [
+ 'options' => Json::class,
+ ];
}
+}
+```
### Value Object Casting
You are not limited to casting values to primitive types. You may also cast values to objects. Defining custom casts that cast values to objects is very similar to casting to primitive types; however, the `set` method should return an array of key / value pairs that will be used to set raw, storable values on the model.
-As an example, we will define a custom cast class that casts multiple model values into a single `Address` value object. We will assume the `Address` value has two public properties: `lineOne` and `lineTwo`:
+As an example, we will define a custom cast class that casts multiple model values into a single `Address` value object. We will assume the `Address` value has two public properties: `lineOne` and `lineTwo`.
- $attributes
- */
- public function get(Model $model, string $key, mixed $value, array $attributes): AddressValueObject
- {
- return new AddressValueObject(
- $attributes['address_line_one'],
- $attributes['address_line_two']
- );
- }
+use App\ValueObjects\Address as AddressValueObject;
+use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
+use Illuminate\Database\Eloquent\Model;
+use InvalidArgumentException;
- /**
- * Prepare the given value for storage.
- *
- * @param array $attributes
- * @return array
- */
- public function set(Model $model, string $key, mixed $value, array $attributes): array
- {
- if (! $value instanceof AddressValueObject) {
- throw new InvalidArgumentException('The given value is not an Address instance.');
- }
+class Address implements CastsAttributes
+{
+ /**
+ * Cast the given value.
+ *
+ * @param array $attributes
+ */
+ public function get(Model $model, string $key, mixed $value, array $attributes): AddressValueObject
+ {
+ return new AddressValueObject(
+ $attributes['address_line_one'],
+ $attributes['address_line_two']
+ );
+ }
- return [
- 'address_line_one' => $value->lineOne,
- 'address_line_two' => $value->lineTwo,
- ];
+ /**
+ * Prepare the given value for storage.
+ *
+ * @param array $attributes
+ * @return array
+ */
+ public function set(Model $model, string $key, mixed $value, array $attributes): array
+ {
+ if (! $value instanceof AddressValueObject) {
+ throw new InvalidArgumentException('The given value is not an Address instance.');
}
- }
-When casting to value objects, any changes made to the value object will automatically be synced back to the model before the model is saved:
+ return [
+ 'address_line_one' => $value->lineOne,
+ 'address_line_two' => $value->lineTwo,
+ ];
+ }
+}
+```
- use App\Models\User;
+```php tab=Usage
+use App\Models\User;
- $user = User::find(1);
+$user = User::find(1);
- $user->address->lineOne = 'Updated Address Value';
+$user->address->lineOne = 'Updated Address Value';
- $user->save();
+$user->save();
+```
> [!NOTE]
> If you plan to serialize your Eloquent models containing value objects to JSON or arrays, you should implement the `Illuminate\Contracts\Support\Arrayable` and `JsonSerializable` interfaces on the value object.
@@ -695,42 +703,44 @@ Therefore, you may specify that your custom cast class will be responsible for s
Occasionally, you may need to write a custom cast class that only transforms values that are being set on the model and does not perform any operations when attributes are being retrieved from the model.
-Inbound only custom casts should implement the `CastsInboundAttributes` interface, which only requires a `set` method to be defined. The `make:cast` Artisan command may be invoked with the `--inbound` option to generate an inbound only cast class:
+Inbound only custom casts should implement the `CastsInboundAttributes` interface, which only requires a `set` method to be defined. The `make:cast` Artisan command may be invoked with the `--inbound` option to generate an inbound only cast class.
+
+A classic example of an inbound only cast is a "hashing" cast. For example, we may define a cast that hashes inbound values via a given algorithm.
-```shell
+```shell tab=Creation
php artisan make:cast Hash --inbound
```
-A classic example of an inbound only cast is a "hashing" cast. For example, we may define a cast that hashes inbound values via a given algorithm:
+```php tab=Definition filename=app/Casts/Hash.php
+ $attributes
+ */
+ public function set(Model $model, string $key, mixed $value, array $attributes): string
{
- /**
- * Create a new cast class instance.
- */
- public function __construct(
- protected string|null $algorithm = null,
- ) {}
-
- /**
- * Prepare the given value for storage.
- *
- * @param array $attributes
- */
- public function set(Model $model, string $key, mixed $value, array $attributes): string
- {
- return is_null($this->algorithm)
- ? bcrypt($value)
- : hash($this->algorithm, $value);
- }
+ return is_null($this->algorithm)
+ ? bcrypt($value)
+ : hash($this->algorithm, $value);
}
+}
+```
### Cast Parameters
diff --git a/eloquent-relationships.md b/eloquent-relationships.md
index caea2bb0645..54e3a20beb0 100644
--- a/eloquent-relationships.md
+++ b/eloquent-relationships.md
@@ -737,19 +737,21 @@ If you would like your intermediate table to have `created_at` and `updated_at`
As noted previously, attributes from the intermediate table may be accessed on models via the `pivot` attribute. However, you are free to customize the name of this attribute to better reflect its purpose within your application.
-For example, if your application contains users that may subscribe to podcasts, you likely have a many-to-many relationship between users and podcasts. If this is the case, you may wish to rename your intermediate table attribute to `subscription` instead of `pivot`. This can be done using the `as` method when defining the relationship:
+For example, if your application contains users that may subscribe to podcasts, you likely have a many-to-many relationship between users and podcasts. If this is the case, you may wish to rename your intermediate table attribute to `subscription` instead of `pivot`. This can be done using the `as` method when defining the relationship. Once the custom intermediate table attribute has been specified, you may access the intermediate table data using the customized name:
- return $this->belongsToMany(Podcast::class)
- ->as('subscription')
- ->withTimestamps();
-
-Once the custom intermediate table attribute has been specified, you may access the intermediate table data using the customized name:
+```php tab=Definition
+return $this->belongsToMany(Podcast::class)
+ ->as('subscription')
+ ->withTimestamps();
+```
- $users = User::with('podcasts')->get();
+```php tab=Usage
+$users = User::with('podcasts')->get();
- foreach ($users->flatMap->podcasts as $podcast) {
- echo $podcast->subscription->created_at;
- }
+foreach ($users->flatMap->podcasts as $podcast) {
+ echo $podcast->subscription->created_at;
+}
+```
### Filtering Queries via Intermediate Table Columns
diff --git a/eloquent-resources.md b/eloquent-resources.md
index ea01e2d81bf..0c44196b0a8 100644
--- a/eloquent-resources.md
+++ b/eloquent-resources.md
@@ -47,44 +47,50 @@ php artisan make:resource UserCollection
> [!NOTE]
> This is a high-level overview of resources and resource collections. You are highly encouraged to read the other sections of this documentation to gain a deeper understanding of the customization and power offered to you by resources.
-Before diving into all of the options available to you when writing resources, let's first take a high-level look at how resources are used within Laravel. A resource class represents a single model that needs to be transformed into a JSON structure. For example, here is a simple `UserResource` resource class:
+Before diving into all of the options available to you when writing resources, let's first take a high-level look at how resources are used within Laravel. A resource class represents a single model that needs to be transformed into a JSON structure. For example, here is a simple `UserResource` resource class.
-
+ */
+ public function toArray(Request $request): array
{
- /**
- * Transform the resource into an array.
- *
- * @return array
- */
- public function toArray(Request $request): array
- {
- return [
- 'id' => $this->id,
- 'name' => $this->name,
- 'email' => $this->email,
- 'created_at' => $this->created_at,
- 'updated_at' => $this->updated_at,
- ];
- }
+ return [
+ 'id' => $this->id,
+ 'name' => $this->name,
+ 'email' => $this->email,
+ 'created_at' => $this->created_at,
+ 'updated_at' => $this->updated_at,
+ ];
}
+}
+```
-Every resource class defines a `toArray` method which returns the array of attributes that should be converted to JSON when the resource is returned as a response from a route or controller method.
+```php tab=Usage filename=routes/web.php
+use App\Http\Resources\UserResource;
+use App\Models\User;
-Note that we can access model properties directly from the `$this` variable. This is because a resource class will automatically proxy property and method access down to the underlying model for convenient access. Once the resource is defined, it may be returned from a route or controller. The resource accepts the underlying model instance via its constructor:
+Route::get('/user/{id}', function (string $id) {
+ return new UserResource(User::findOrFail($id));
+});
+```
- use App\Http\Resources\UserResource;
- use App\Models\User;
+Every resource class defines a `toArray` method which returns the array of attributes that should be converted to JSON when the resource is returned as a response from a route or controller method.
- Route::get('/user/{id}', function (string $id) {
- return new UserResource(User::findOrFail($id));
- });
+Note that we can access model properties directly from the `$this` variable. This is because a resource class will automatically proxy property and method access down to the underlying model for convenient access.
### Resource Collections
@@ -98,47 +104,47 @@ If you are returning a collection of resources or a paginated response, you shou
return UserResource::collection(User::all());
});
-Note that this does not allow any addition of custom meta data that may need to be returned with your collection. If you would like to customize the resource collection response, you may create a dedicated resource to represent the collection:
+Note that this does not allow any addition of custom meta data that may need to be returned with your collection. If you would like to customize the resource collection response, you may create a dedicated resource to represent the collection. Once the resource collection class has been generated, you may easily define any meta data that should be included with the response. After defining your resource collection, it may be returned from a route or controller.
-```shell
+```shell tab=Creation
php artisan make:resource UserCollection
```
-Once the resource collection class has been generated, you may easily define any meta data that should be included with the response:
+```php tab=Definition filename=app/Http/Resources/UserCollection.php
+
+ */
+ public function toArray(Request $request): array
{
- /**
- * Transform the resource collection into an array.
- *
- * @return array
- */
- public function toArray(Request $request): array
- {
- return [
- 'data' => $this->collection,
- 'links' => [
- 'self' => 'link-value',
- ],
- ];
- }
+ return [
+ 'data' => $this->collection,
+ 'links' => [
+ 'self' => 'link-value',
+ ],
+ ];
}
+}
+```
-After defining your resource collection, it may be returned from a route or controller:
-
- use App\Http\Resources\UserCollection;
- use App\Models\User;
+```php tab=Usage filename=routes/web.php
+use App\Http\Resources\UserCollection;
+use App\Models\User;
- Route::get('/users', function () {
- return new UserCollection(User::all());
- });
+Route::get('/users', function () {
+ return new UserCollection(User::all());
+});
+```
#### Preserving Collection Keys
diff --git a/eloquent.md b/eloquent.md
index 776029bc98b..2fb7357e121 100644
--- a/eloquent.md
+++ b/eloquent.md
@@ -248,19 +248,23 @@ You can override the UUID generation process for a given model by defining a `ne
If you wish, you may choose to utilize "ULIDs" instead of UUIDs. ULIDs are similar to UUIDs; however, they are only 26 characters in length. Like ordered UUIDs, ULIDs are lexicographically sortable for efficient database indexing. To utilize ULIDs, you should use the `Illuminate\Database\Eloquent\Concerns\HasUlids` trait on your model. You should also ensure that the model has a [ULID equivalent primary key column](/docs/{{version}}/migrations#column-method-ulid):
- use Illuminate\Database\Eloquent\Concerns\HasUlids;
- use Illuminate\Database\Eloquent\Model;
+```php tab=Definition filename=app/Models/Article.php
+use Illuminate\Database\Eloquent\Concerns\HasUlids;
+use Illuminate\Database\Eloquent\Model;
- class Article extends Model
- {
- use HasUlids;
+class Article extends Model
+{
+ use HasUlids;
- // ...
- }
+ // ...
+}
+```
- $article = Article::create(['title' => 'Traveling to Asia']);
+```php tab=Usage
+$article = Article::create(['title' => 'Traveling to Asia']);
- $article->id; // "01gd4d3tgrrfqeda94gdbtdk5c"
+$article->id; // "01gd4d3tgrrfqeda94gdbtdk5c"
+```
### Timestamps
@@ -1488,73 +1492,77 @@ If needed, you may utilize [queueable anonymous event listeners](/docs/{{version
#### Defining Observers
-If you are listening for many events on a given model, you may use observers to group all of your listeners into a single class. Observer classes have method names which reflect the Eloquent events you wish to listen for. Each of these methods receives the affected model as their only argument. The `make:observer` Artisan command is the easiest way to create a new observer class:
+If you are listening for many events on a given model, you may use observers to group all of your listeners into a single class. Observer classes have method names which reflect the Eloquent events you wish to listen for. Each of these methods receives the affected model as their only argument. The `make:observer` Artisan command is the easiest way to create a new observer class.
-```shell
+This command will place the new observer in your `app/Observers` directory. If this directory does not exist, Artisan will create it for you. Your fresh observer will look like the following.
+
+To register an observer, you may place the `ObservedBy` attribute on the corresponding model.
+
+```shell tab=Definition
php artisan make:observer UserObserver --model=User
```
-This command will place the new observer in your `app/Observers` directory. If this directory does not exist, Artisan will create it for you. Your fresh observer will look like the following:
-
-
#### S3 Driver Configuration
-Before using the S3 driver, you will need to install the Flysystem S3 package via the Composer package manager:
+Before using the S3 driver, you will need to install the Flysystem S3 package via the Composer package manager.
-```shell
+An S3 disk configuration array is located in your `config/filesystems.php` configuration file. Typically, you should configure your S3 information and credentials using the following environment variables which are referenced by the `config/filesystems.php` configuration file.
+
+```shell tab=Installation
composer require league/flysystem-aws-s3-v3 "^3.0" --with-all-dependencies
```
-An S3 disk configuration array is located in your `config/filesystems.php` configuration file. Typically, you should configure your S3 information and credentials using the following environment variables which are referenced by the `config/filesystems.php` configuration file:
-
-```
+```ini tab=Configuration filename=.env
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
@@ -106,77 +106,81 @@ For convenience, these environment variables match the naming convention used by
#### FTP Driver Configuration
-Before using the FTP driver, you will need to install the Flysystem FTP package via the Composer package manager:
+Before using the FTP driver, you will need to install the Flysystem FTP package via the Composer package manager.
-```shell
+Laravel's Flysystem integrations work great with FTP; however, a sample configuration is not included with the framework's default `config/filesystems.php` configuration file. If you need to configure an FTP filesystem, you may use the configuration example below.
+
+```shell tab=Installation
composer require league/flysystem-ftp "^3.0"
```
-Laravel's Flysystem integrations work great with FTP; however, a sample configuration is not included with the framework's default `config/filesystems.php` configuration file. If you need to configure an FTP filesystem, you may use the configuration example below:
-
- 'ftp' => [
- 'driver' => 'ftp',
- 'host' => env('FTP_HOST'),
- 'username' => env('FTP_USERNAME'),
- 'password' => env('FTP_PASSWORD'),
-
- // Optional FTP Settings...
- // 'port' => env('FTP_PORT', 21),
- // 'root' => env('FTP_ROOT'),
- // 'passive' => true,
- // 'ssl' => true,
- // 'timeout' => 30,
- ],
+```php tab=Configuration filename=config/filesystem.php
+'ftp' => [
+ 'driver' => 'ftp',
+ 'host' => env('FTP_HOST'),
+ 'username' => env('FTP_USERNAME'),
+ 'password' => env('FTP_PASSWORD'),
+
+ // Optional FTP Settings...
+ // 'port' => env('FTP_PORT', 21),
+ // 'root' => env('FTP_ROOT'),
+ // 'passive' => true,
+ // 'ssl' => true,
+ // 'timeout' => 30,
+],
+```
#### SFTP Driver Configuration
-Before using the SFTP driver, you will need to install the Flysystem SFTP package via the Composer package manager:
+Before using the SFTP driver, you will need to install the Flysystem SFTP package via the Composer package manager.
-```shell
+Laravel's Flysystem integrations work great with SFTP; however, a sample configuration is not included with the framework's default `config/filesystems.php` configuration file. If you need to configure an SFTP filesystem, you may use the configuration example below.
+
+```shell tab=Installation
composer require league/flysystem-sftp-v3 "^3.0"
```
-Laravel's Flysystem integrations work great with SFTP; however, a sample configuration is not included with the framework's default `config/filesystems.php` configuration file. If you need to configure an SFTP filesystem, you may use the configuration example below:
-
- 'sftp' => [
- 'driver' => 'sftp',
- 'host' => env('SFTP_HOST'),
-
- // Settings for basic authentication...
- 'username' => env('SFTP_USERNAME'),
- 'password' => env('SFTP_PASSWORD'),
-
- // Settings for SSH key based authentication with encryption password...
- 'privateKey' => env('SFTP_PRIVATE_KEY'),
- 'passphrase' => env('SFTP_PASSPHRASE'),
-
- // Settings for file / directory permissions...
- 'visibility' => 'private', // `private` = 0600, `public` = 0644
- 'directory_visibility' => 'private', // `private` = 0700, `public` = 0755
-
- // Optional SFTP Settings...
- // 'hostFingerprint' => env('SFTP_HOST_FINGERPRINT'),
- // 'maxTries' => 4,
- // 'passphrase' => env('SFTP_PASSPHRASE'),
- // 'port' => env('SFTP_PORT', 22),
- // 'root' => env('SFTP_ROOT', ''),
- // 'timeout' => 30,
- // 'useAgent' => true,
- ],
+```php tab=Configuration filename=config/filesystem.php
+'sftp' => [
+ 'driver' => 'sftp',
+ 'host' => env('SFTP_HOST'),
+
+ // Settings for basic authentication...
+ 'username' => env('SFTP_USERNAME'),
+ 'password' => env('SFTP_PASSWORD'),
+
+ // Settings for SSH key based authentication with encryption password...
+ 'privateKey' => env('SFTP_PRIVATE_KEY'),
+ 'passphrase' => env('SFTP_PASSPHRASE'),
+
+ // Settings for file / directory permissions...
+ 'visibility' => 'private', // `private` = 0600, `public` = 0644
+ 'directory_visibility' => 'private', // `private` = 0700, `public` = 0755
+
+ // Optional SFTP Settings...
+ // 'hostFingerprint' => env('SFTP_HOST_FINGERPRINT'),
+ // 'maxTries' => 4,
+ // 'passphrase' => env('SFTP_PASSPHRASE'),
+ // 'port' => env('SFTP_PORT', 22),
+ // 'root' => env('SFTP_ROOT', ''),
+ // 'timeout' => 30,
+ // 'useAgent' => true,
+],
+```
### Scoped and Read-Only Filesystems
-Scoped disks allow you to define a filesystem where all paths are automatically prefixed with a given path prefix. Before creating a scoped filesystem disk, you will need to install an additional Flysystem package via the Composer package manager:
+Scoped disks allow you to define a filesystem where all paths are automatically prefixed with a given path prefix. Before creating a scoped filesystem disk, you will need to install an additional Flysystem package via the Composer package manager.
-```shell
+You may create a path scoped instance of any existing filesystem disk by defining a disk that utilizes the `scoped` driver. For example, you may create a disk which scopes your existing `s3` disk to a specific path prefix, and then every file operation using your scoped disk will utilize the specified prefix:
+
+```shell tab=Installation
composer require league/flysystem-path-prefixing "^3.0"
```
-You may create a path scoped instance of any existing filesystem disk by defining a disk that utilizes the `scoped` driver. For example, you may create a disk which scopes your existing `s3` disk to a specific path prefix, and then every file operation using your scoped disk will utilize the specified prefix:
-
-```php
+```php tab=Configuration filename=config/filesystem.php
's3-videos' => [
'driver' => 'scoped',
'disk' => 's3',
@@ -184,15 +188,13 @@ You may create a path scoped instance of any existing filesystem disk by definin
],
```
-"Read-only" disks allow you to create filesystem disks that do not allow write operations. Before using the `read-only` configuration option, you will need to install an additional Flysystem package via the Composer package manager:
+"Read-only" disks allow you to create filesystem disks that do not allow write operations. Before using the `read-only` configuration option, you will need to install an additional Flysystem package via the Composer package manager. Next, you may include the `read-only` configuration option in one or more of your disk's configuration arrays:
-```shell
+```shell tab=Installation
composer require league/flysystem-read-only "^3.0"
```
-Next, you may include the `read-only` configuration option in one or more of your disk's configuration arrays:
-
-```php
+```php tab=Configuration filename=config/filesystem.php
's3-videos' => [
'driver' => 's3',
// ...
@@ -750,54 +752,54 @@ By default, the `fake` method will delete all files in its temporary directory.
Laravel's Flysystem integration provides support for several "drivers" out of the box; however, Flysystem is not limited to these and has adapters for many other storage systems. You can create a custom driver if you want to use one of these additional adapters in your Laravel application.
-In order to define a custom filesystem you will need a Flysystem adapter. Let's add a community maintained Dropbox adapter to our project:
+In order to define a custom filesystem you will need a Flysystem adapter. Let's add a community maintained Dropbox adapter to our project. Next, you can register the driver within the `boot` method of one of your application's [service providers](/docs/{{version}}/providers). To accomplish this, you should use the `extend` method of the `Storage` facade:
-```shell
+```shell tab=Installation
composer require spatie/flysystem-dropbox
```
-Next, you can register the driver within the `boot` method of one of your application's [service providers](/docs/{{version}}/providers). To accomplish this, you should use the `extend` method of the `Storage` facade:
-
- [!WARNING]
> Laravel Horizon requires that you use [Redis](https://redis.io) to power your queue. Therefore, you should ensure that your queue connection is set to `redis` in your application's `config/queue.php` configuration file.
-You may install Horizon into your project using the Composer package manager:
+You may install Horizon into your project using the Composer package manager. After installing Horizon, publish its assets using the `horizon:install` Artisan command.
-```shell
+```shell tab=Installation
composer require laravel/horizon
```
-After installing Horizon, publish its assets using the `horizon:install` Artisan command:
-
-```shell
+```shell tab=Setup
php artisan horizon:install
```
diff --git a/mail.md b/mail.md
index 2798dad7bfc..aa6c6507613 100644
--- a/mail.md
+++ b/mail.md
@@ -53,29 +53,33 @@ The API based drivers such as Mailgun, Postmark, Resend, and MailerSend are ofte
#### Mailgun Driver
-To use the Mailgun driver, install Symfony's Mailgun Mailer transport via Composer:
+To use the Mailgun driver,
-```shell
+1. install Symfony's Mailgun Mailer transport via Composer
+2. Next, set the `default` option in your application's `config/mail.php` configuration file to `mailgun` and add the following configuration array to your array of `mailers`
+3. After configuring your application's default mailer, add the following options to your `config/services.php` configuration file
+
+```shell tab=Installation
composer require symfony/mailgun-mailer symfony/http-client
```
-Next, set the `default` option in your application's `config/mail.php` configuration file to `mailgun` and add the following configuration array to your array of `mailers`:
-
- 'mailgun' => [
- 'transport' => 'mailgun',
- // 'client' => [
- // 'timeout' => 5,
- // ],
- ],
-
-After configuring your application's default mailer, add the following options to your `config/services.php` configuration file:
+```php tab=Mailer configuration filename=config/mail.php
+'mailgun' => [
+ 'transport' => 'mailgun',
+ // 'client' => [
+ // 'timeout' => 5,
+ // ],
+],
+```
- 'mailgun' => [
- 'domain' => env('MAILGUN_DOMAIN'),
- 'secret' => env('MAILGUN_SECRET'),
- 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
- 'scheme' => 'https',
- ],
+```php tab=Service configuration filename=config/servives.php
+'mailgun' => [
+ 'domain' => env('MAILGUN_DOMAIN'),
+ 'secret' => env('MAILGUN_SECRET'),
+ 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
+ 'scheme' => 'https',
+],
+```
If you are not using the United States [Mailgun region](https://documentation.mailgun.com/en/latest/api-intro.html#mailgun-regions), you may define your region's endpoint in the `services` configuration file:
@@ -89,17 +93,17 @@ If you are not using the United States [Mailgun region](https://documentation.ma
#### Postmark Driver
-To use the [Postmark](https://postmarkapp.com/) driver, install Symfony's Postmark Mailer transport via Composer:
+To use the [Postmark](https://postmarkapp.com/) driver, install Symfony's Postmark Mailer transport via Composer. Next, set the `default` option in your application's `config/mail.php` configuration file to `postmark`. After configuring your application's default mailer, ensure that your `config/services.php` configuration file contains the following options.
-```shell
+```shell tab=Installation
composer require symfony/postmark-mailer symfony/http-client
```
-Next, set the `default` option in your application's `config/mail.php` configuration file to `postmark`. After configuring your application's default mailer, ensure that your `config/services.php` configuration file contains the following options:
-
- 'postmark' => [
- 'token' => env('POSTMARK_TOKEN'),
- ],
+```php tab=Configuration filename=config/services.php
+'postmark' => [
+ 'token' => env('POSTMARK_TOKEN'),
+],
+```
If you would like to specify the Postmark message stream that should be used by a given mailer, you may add the `message_stream_id` configuration option to the mailer's configuration array. This configuration array can be found in your application's `config/mail.php` configuration file:
@@ -116,34 +120,34 @@ This way you are also able to set up multiple Postmark mailers with different me
#### Resend Driver
-To use the [Resend](https://resend.com/) driver, install Resend's PHP SDK via Composer:
+To use the [Resend](https://resend.com/) driver, install Resend's PHP SDK via Composer. Next, set the `default` option in your application's `config/mail.php` configuration file to `resend`. After configuring your application's default mailer, ensure that your `config/services.php` configuration file contains the following options.
-```shell
+```shell tab=Installation
composer require resend/resend-php
```
-Next, set the `default` option in your application's `config/mail.php` configuration file to `resend`. After configuring your application's default mailer, ensure that your `config/services.php` configuration file contains the following options:
-
- 'resend' => [
- 'key' => env('RESEND_KEY'),
- ],
+```php tab=Configuration filename=config/services.php
+'resend' => [
+ 'key' => env('RESEND_KEY'),
+],
+```
#### SES Driver
-To use the Amazon SES driver you must first install the Amazon AWS SDK for PHP. You may install this library via the Composer package manager:
+To use the Amazon SES driver you must first install the Amazon AWS SDK for PHP. You may install this library via the Composer package manager. Next, set the `default` option in your `config/mail.php` configuration file to `ses` and verify that your `config/services.php` configuration file contains the following options.
-```shell
+```shell tab=Installation
composer require aws/aws-sdk-php
```
-Next, set the `default` option in your `config/mail.php` configuration file to `ses` and verify that your `config/services.php` configuration file contains the following options:
-
- 'ses' => [
- 'key' => env('AWS_ACCESS_KEY_ID'),
- 'secret' => env('AWS_SECRET_ACCESS_KEY'),
- 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
- ],
+```php tab=Configuration filename=config/services.php
+'ses' => [
+ 'key' => env('AWS_ACCESS_KEY_ID'),
+ 'secret' => env('AWS_SECRET_ACCESS_KEY'),
+ 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
+],
+```
To utilize AWS [temporary credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html) via a session token, you may add a `token` key to your application's SES configuration:
@@ -187,15 +191,15 @@ If you would like to define [additional options](https://docs.aws.amazon.com/aws
#### MailerSend Driver
-[MailerSend](https://www.mailersend.com/), a transactional email and SMS service, maintains their own API based mail driver for Laravel. The package containing the driver may be installed via the Composer package manager:
+[MailerSend](https://www.mailersend.com/), a transactional email and SMS service, maintains their own API based mail driver for Laravel. The package containing the driver may be installed via the Composer package manager. Once the package is installed, add the `MAILERSEND_API_KEY` environment variable to your application's `.env` file. In addition, the `MAIL_MAILER` environment variable should be defined as `mailersend`.
-```shell
+Finally, add MailerSend to the `mailers` array in your application's `config/mail.php` configuration file:
+
+```shell tab=Installation
composer require mailersend/laravel-driver
```
-Once the package is installed, add the `MAILERSEND_API_KEY` environment variable to your application's `.env` file. In addition, the `MAIL_MAILER` environment variable should be defined as `mailersend`:
-
-```shell
+```ini tab=Environment variables filename=.env
MAIL_MAILER=mailersend
MAIL_FROM_ADDRESS=app@yourdomain.com
MAIL_FROM_NAME="App Name"
@@ -203,9 +207,7 @@ MAIL_FROM_NAME="App Name"
MAILERSEND_API_KEY=your-api-key
```
-Finally, add MailerSend to the `mailers` array in your application's `config/mail.php` configuration file:
-
-```php
+```php tab=Configuration filename=config/mail..php
'mailersend' => [
'transport' => 'mailersend',
],
@@ -724,28 +726,28 @@ Markdown mailable messages allow you to take advantage of the pre-built template
### Generating Markdown Mailables
-To generate a mailable with a corresponding Markdown template, you may use the `--markdown` option of the `make:mail` Artisan command:
+To generate a mailable with a corresponding Markdown template, you may use the `--markdown` option of the `make:mail` Artisan command. Then, when configuring the mailable `Content` definition within its `content` method, use the `markdown` parameter instead of the `view` parameter.
-```shell
+```shell tab=Creation
php artisan make:mail OrderShipped --markdown=mail.orders.shipped
```
-Then, when configuring the mailable `Content` definition within its `content` method, use the `markdown` parameter instead of the `view` parameter:
-
- use Illuminate\Mail\Mailables\Content;
+```php tab=Usage filename=app/Mail/OrderShipped.php
+use Illuminate\Mail\Mailables\Content;
- /**
- * Get the message content definition.
- */
- public function content(): Content
- {
- return new Content(
- markdown: 'mail.orders.shipped',
- with: [
- 'url' => $this->orderUrl,
- ],
- );
- }
+/**
+ * Get the message content definition.
+ */
+public function content(): Content
+{
+ return new Content(
+ markdown: 'mail.orders.shipped',
+ with: [
+ 'url' => $this->orderUrl,
+ ],
+ );
+}
+```
### Writing Markdown Messages
diff --git a/middleware.md b/middleware.md
index 95db7bb8b7d..6dd44781500 100644
--- a/middleware.md
+++ b/middleware.md
@@ -21,38 +21,48 @@ Additional middleware can be written to perform a variety of tasks besides authe
## Defining Middleware
-To create a new middleware, use the `make:middleware` Artisan command:
+1. To create a new middleware, use the `make:middleware` Artisan command
+2. This command will place a new `EnsureTokenIsValid` class within your `app/Http/Middleware` directory. In this middleware, we will only allow access to the route if the supplied `token` input matches a specified value. Otherwise, we will redirect the users back to the `/home` URI.
+3. If you would like to assign middleware to specific routes, you may invoke the `middleware` method when defining the route (there are other options, we'll explore below)
-```shell
+```shell tab=Creation
php artisan make:middleware EnsureTokenIsValid
```
-This command will place a new `EnsureTokenIsValid` class within your `app/Http/Middleware` directory. In this middleware, we will only allow access to the route if the supplied `token` input matches a specified value. Otherwise, we will redirect the users back to the `/home` URI:
+```php tab=Definition filename=app/Http/Middleware/EnsureTokenIsValid.php
+input('token') !== 'my-secret-token') {
- return redirect('/home');
- }
-
- return $next($request);
+ if ($request->input('token') !== 'my-secret-token') {
+ return redirect('/home');
}
+
+ return $next($request);
}
+}
+```
+
+```php tab=Usage filename=routes/web.php
+use App\Http\Middleware\EnsureTokenIsValid;
+
+Route::get('/profile', function () {
+ // ...
+})->middleware(EnsureTokenIsValid::class);
+```
As you can see, if the given `token` does not match our secret token, the middleware will return an HTTP redirect to the client; otherwise, the request will be passed further into the application. To pass the request deeper into the application (allowing the middleware to "pass"), you should call the `$next` callback with the `$request`.
diff --git a/notifications.md b/notifications.md
index 995d404cb2f..206649221f0 100644
--- a/notifications.md
+++ b/notifications.md
@@ -711,25 +711,27 @@ Markdown mail notifications allow you to take advantage of the pre-built templat
### Generating the Message
-To generate a notification with a corresponding Markdown template, you may use the `--markdown` option of the `make:notification` Artisan command:
+To generate a notification with a corresponding Markdown template, you may use the `--markdown` option of the `make:notification` Artisan command.
-```shell
+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:
+
+```shell tab=Creation
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. 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.
- */
- public function toMail(object $notifiable): MailMessage
- {
- $url = url('/invoice/'.$this->invoice->id);
+```php tab=Definition filename=app/Notifications/InvoicePaid.php
+/**
+ * Get the mail representation of the notification.
+ */
+public function toMail(object $notifiable): MailMessage
+{
+ $url = url('/invoice/'.$this->invoice->id);
- return (new MailMessage)
- ->subject('Invoice Paid')
- ->markdown('mail.invoice.paid', ['url' => $url]);
- }
+ return (new MailMessage)
+ ->subject('Invoice Paid')
+ ->markdown('mail.invoice.paid', ['url' => $url]);
+}
+```
### Writing the Message
@@ -1118,11 +1120,7 @@ To route Vonage notifications to the proper phone number, define a `routeNotific
### Prerequisites
-Before sending Slack notifications, you should install the Slack notification channel via Composer:
-
-```shell
-composer require laravel/slack-notification-channel
-```
+Before sending Slack notifications, you should install the Slack notification channel via Composer.
Additionally, you must create a [Slack App](https://api.slack.com/apps?new_app=1) for your Slack workspace.
@@ -1130,12 +1128,18 @@ If you only need to send notifications to the same Slack workspace that the App
Next, copy the App's "Bot User OAuth Token" and place it within a `slack` configuration array in your application's `services.php` configuration file. This token can be found on the "OAuth & Permissions" tab within Slack:
- 'slack' => [
- 'notifications' => [
- 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
- 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
- ],
+```shell tab=Installation
+composer require laravel/slack-notification-channel
+```
+
+```php tab=Configuration filename=config/services.php
+'slack' => [
+ 'notifications' => [
+ 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'),
+ 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
],
+],
+```
#### App Distribution
diff --git a/passport.md b/passport.md
index 96738eae41b..4b8764c2ec5 100644
--- a/passport.md
+++ b/passport.md
@@ -61,46 +61,50 @@ However, if you are attempting to authenticate a single-page application, mobile
## Installation
-You may install Laravel Passport via the `install:api` Artisan command:
-
-```shell
-php artisan install:api --passport
-```
+You may install Laravel Passport via the `install:api` Artisan command.
This command will publish and run the database migrations necessary for creating the tables your application needs to store OAuth2 clients and access tokens. The command will also create the encryption keys required to generate secure access tokens.
Additionally, this command will ask if you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers.
-After running the `install:api` command, add the `Laravel\Passport\HasApiTokens` trait to your `App\Models\User` model. This trait will provide a few helper methods to your model which allow you to inspect the authenticated user's token and scopes:
+After running the `install:api` command, add the `Laravel\Passport\HasApiTokens` trait to your `App\Models\User` model. This trait will provide a few helper methods to your model which allow you to inspect the authenticated user's token and scopes.
- [
- 'web' => [
- 'driver' => 'session',
- 'provider' => 'users',
- ],
+class User extends Authenticatable
+{
+ use HasApiTokens, HasFactory, Notifiable;
+}
+```
- 'api' => [
- 'driver' => 'passport',
- 'provider' => 'users',
- ],
+```php tab=Configuration filename=config/auth.php
+'guards' => [
+ 'web' => [
+ 'driver' => 'session',
+ 'provider' => 'users',
],
+ 'api' => [
+ 'driver' => 'passport',
+ 'provider' => 'users',
+ ],
+],
+```
+
### Deploying Passport
@@ -781,27 +785,31 @@ Once the grant has been enabled, developers may use their client ID to request a
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` Artisan 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.
-```shell
-php artisan passport:client --client
-```
+Next, to use this grant type, register a middleware alias for the `CheckClientCredentials` middleware. You may define middleware aliases in your application's `bootstrap/app.php` file.
-Next, to use this grant type, register a middleware alias for the `CheckClientCredentials` middleware. You may define middleware aliases in your application's `bootstrap/app.php` file:
+Then, attach the middleware to a route.
- use Laravel\Passport\Http\Middleware\CheckClientCredentials;
+```shell tab=Installation
+php artisan passport:client --client
+```
- ->withMiddleware(function (Middleware $middleware) {
- $middleware->alias([
- 'client' => CheckClientCredentials::class
- ]);
- })
+```php tab=Definition filename=bootstrap/app.php
+use Laravel\Passport\Http\Middleware\CheckClientCredentials;
-Then, attach the middleware to a route:
+->withMiddleware(function (Middleware $middleware) {
+ $middleware->alias([
+ 'client' => CheckClientCredentials::class
+ ]);
+})
+```
- Route::get('/orders', function (Request $request) {
- ...
- })->middleware('client');
+```php tab=Usage filename=routes/web.php
+Route::get('/orders', function (Request $request) {
+ ...
+})->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:
@@ -836,15 +844,13 @@ 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 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:
+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. After creating your personal access client, place the client's ID and plain-text secret value in your application's `.env` file:
-```shell
+```shell tab=Installation
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:
-
-```ini
+```ini tab=Configuration
PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value"
PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value"
```
diff --git a/pennant.md b/pennant.md
index c0783ae1b3f..637b2947438 100644
--- a/pennant.md
+++ b/pennant.md
@@ -111,15 +111,15 @@ For convenience, if a feature definition only returns a lottery, you may omit th
### Class Based Features
-Pennant also allows you to define class based features. Unlike closure based feature definitions, there is no need to register a class based feature in a service provider. To create a class based feature, you may invoke the `pennant:feature` Artisan command. By default the feature class will be placed in your application's `app/Features` directory:
+Pennant also allows you to define class based features. Unlike closure based feature definitions, there is no need to register a class based feature in a service provider. To create a class based feature, you may invoke the `pennant:feature` Artisan command. By default the feature class will be placed in your application's `app/Features` directory.
-```shell
+When writing a feature class, you only need to define a `resolve` method, which will be invoked to resolve the feature's initial value for a given scope. Again, the scope will typically be the currently authenticated user.
+
+```shell tab=Installation
php artisan pennant:feature NewApi
```
-When writing a feature class, you only need to define a `resolve` method, which will be invoked to resolve the feature's initial value for a given scope. Again, the scope will typically be the currently authenticated user:
-
-```php
+```php tab=Definition
[!NOTE]
> If you would like a head start when developing your Laravel application with React and Inertia, consider using one of our [starter kits](/docs/{{version}}/starter-kits). Laravel's starter kits provide backend and frontend authentication scaffolding for your new Laravel application.
-Before using Precognition with React and Inertia, be sure to review our general documentation on [using Precognition with React](#using-react). When using React with Inertia, you will need to install the Inertia compatible Precognition library via NPM:
-
-```shell
-npm install laravel-precognition-react-inertia
-```
+Before using Precognition with React and Inertia, be sure to review our general documentation on [using Precognition with React](#using-react). When using React with Inertia, you will need to install the Inertia compatible Precognition library via NPM.
Once installed, Precognition's `useForm` function will return an Inertia [form helper](https://inertiajs.com/forms#form-helper) augmented with the validation features discussed above.
The form helper's `submit` method has been streamlined, removing the need to specify the HTTP method or URL. Instead, you may pass Inertia's [visit options](https://inertiajs.com/manual-visits) as the first and only argument. In addition, the `submit` method does not return a Promise as seen in the React example above. Instead, you may provide any of Inertia's supported [event callbacks](https://inertiajs.com/manual-visits#event-callbacks) in the visit options given to the `submit` method:
-```js
+```shell tab=Installation
+npm install laravel-precognition-react-inertia
+```
+
+```js tab=Usage
import { useForm } from 'laravel-precognition-react-inertia';
const form = useForm('post', '/users', {
diff --git a/queues.md b/queues.md
index b6dfffd8c1c..3c940bef6e3 100644
--- a/queues.md
+++ b/queues.md
@@ -1609,15 +1609,13 @@ In addition, you may define `ttl` attribute for your table if you would like to
#### DynamoDB Configuration
-Next, install the AWS SDK so that your Laravel application can communicate with Amazon DynamoDB:
+Next, install the AWS SDK so that your Laravel application can communicate with Amazon DynamoDB. Then, set the `queue.batching.driver` configuration option's value to `dynamodb`. In addition, you should define `key`, `secret`, and `region` configuration options within the `batching` configuration array. These options will be used to authenticate with AWS. When using the `dynamodb` driver, the `queue.batching.database` configuration option is unnecessary:
-```shell
+```shell tab=Installtion
composer require aws/aws-sdk-php
```
-Then, set the `queue.batching.driver` configuration option's value to `dynamodb`. In addition, you should define `key`, `secret`, and `region` configuration options within the `batching` configuration array. These options will be used to authenticate with AWS. When using the `dynamodb` driver, the `queue.batching.database` configuration option is unnecessary:
-
-```php
+```php tab=Configuration filename=config/queue.php
'batching' => [
'driver' => env('QUEUE_BATCHING_DRIVER', 'dynamodb'),
'key' => env('AWS_ACCESS_KEY_ID'),
@@ -2075,15 +2073,13 @@ Laravel also provides support for storing your failed job records in [DynamoDB](
The `failed_jobs` table should have a string primary partition key named `application` and a string primary sort key named `uuid`. The `application` portion of the key will contain your application's name as defined by the `name` configuration value within your application's `app` configuration file. Since the application name is part of the DynamoDB table's key, you can use the same table to store failed jobs for multiple Laravel applications.
-In addition, ensure that you install the AWS SDK so that your Laravel application can communicate with Amazon DynamoDB:
+In addition, ensure that you install the AWS SDK so that your Laravel application can communicate with Amazon DynamoDB. Next, set the `queue.failed.driver` configuration option's value to `dynamodb`. In addition, you should define `key`, `secret`, and `region` configuration options within the failed job configuration array. These options will be used to authenticate with AWS. When using the `dynamodb` driver, the `queue.failed.database` configuration option is unnecessary:
-```shell
+```shell tab=Installation
composer require aws/aws-sdk-php
```
-Next, set the `queue.failed.driver` configuration option's value to `dynamodb`. In addition, you should define `key`, `secret`, and `region` configuration options within the failed job configuration array. These options will be used to authenticate with AWS. When using the `dynamodb` driver, the `queue.failed.database` configuration option is unnecessary:
-
-```php
+```php tab=Configuration filename=config/queue.php
'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'dynamodb'),
'key' => env('AWS_ACCESS_KEY_ID'),
diff --git a/requests.md b/requests.md
index bfcc95d2467..00ca2d0f8ff 100644
--- a/requests.md
+++ b/requests.md
@@ -219,20 +219,22 @@ Since many applications only serve HTML or JSON, you may use the `expectsJson` m
### PSR-7 Requests
-The [PSR-7 standard](https://www.php-fig.org/psr/psr-7/) specifies interfaces for HTTP messages, including requests and responses. If you would like to obtain an instance of a PSR-7 request instead of a Laravel request, you will first need to install a few libraries. Laravel uses the *Symfony HTTP Message Bridge* component to convert typical Laravel requests and responses into PSR-7 compatible implementations:
+The [PSR-7 standard](https://www.php-fig.org/psr/psr-7/) specifies interfaces for HTTP messages, including requests and responses. If you would like to obtain an instance of a PSR-7 request instead of a Laravel request, you will first need to install a few libraries. Laravel uses the *Symfony HTTP Message Bridge* component to convert typical Laravel requests and responses into PSR-7 compatible implementations.
-```shell
+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:
+
+```shell tab=Installation
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:
-
- use Psr\Http\Message\ServerRequestInterface;
+```php tab=Usage filename=routes/web.php
+use Psr\Http\Message\ServerRequestInterface;
- Route::get('/', function (ServerRequestInterface $request) {
- // ...
- });
+Route::get('/', function (ServerRequestInterface $request) {
+ // ...
+});
+```
> [!NOTE]
> 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.
diff --git a/scout.md b/scout.md
index 65af70930a7..3d70b345953 100644
--- a/scout.md
+++ b/scout.md
@@ -105,15 +105,13 @@ composer require algolia/algoliasearch-client-php
[Meilisearch](https://www.meilisearch.com) is a blazingly fast and open source search engine. If you aren't sure how to install Meilisearch on your local machine, you may use [Laravel Sail](/docs/{{version}}/sail#meilisearch), Laravel's officially supported Docker development environment.
-When using the Meilisearch driver you will need to install the Meilisearch PHP SDK via the Composer package manager:
+When using the Meilisearch driver you will need to install the Meilisearch PHP SDK via the Composer package manager. Then, set the `SCOUT_DRIVER` environment variable as well as your Meilisearch `host` and `key` credentials within your application's `.env` file:
-```shell
+```shell tab=Installation
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
```
-Then, set the `SCOUT_DRIVER` environment variable as well as your Meilisearch `host` and `key` credentials within your application's `.env` file:
-
-```ini
+```ini tab=Configuration filename=.env
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey
@@ -133,15 +131,13 @@ In addition, you should ensure that you install a version of `meilisearch/meilis
You can [self-host](https://typesense.org/docs/guide/install-typesense.html#option-2-local-machine-self-hosting) Typesense or use [Typesense Cloud](https://cloud.typesense.org).
-To get started using Typesense with Scout, install the Typesense PHP SDK via the Composer package manager:
+To get started using Typesense with Scout, install the Typesense PHP SDK via the Composer package manager. Then, set the `SCOUT_DRIVER` environment variable as well as your Typesense host and API key credentials within your application's .env file:
-```shell
+```shell tab=Installation
composer require typesense/typesense-php
```
-Then, set the `SCOUT_DRIVER` environment variable as well as your Typesense host and API key credentials within your application's .env file:
-
-```env
+```ini tab=Configuration filename=.env
SCOUT_DRIVER=typesense
TYPESENSE_API_KEY=masterKey
TYPESENSE_HOST=localhost
diff --git a/seeding.md b/seeding.md
index 400bbcb308d..fc7c39bb008 100644
--- a/seeding.md
+++ b/seeding.md
@@ -18,39 +18,41 @@ Laravel includes the ability to seed your database with data using seed classes.
## Writing Seeders
-To generate a seeder, execute the `make:seeder` [Artisan command](/docs/{{version}}/artisan). All seeders generated by the framework will be placed in the `database/seeders` directory:
-
-```shell
-php artisan make:seeder UserSeeder
-```
+To generate a seeder, execute the `make:seeder` [Artisan command](/docs/{{version}}/artisan). All seeders generated by the framework will be placed in the `database/seeders` directory.
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}}/eloquent-factories).
-As an example, let's modify the default `DatabaseSeeder` class and add a database insert statement to the `run` method:
+As an example, let's modify the default `DatabaseSeeder` class and add a database insert statement to the `run` method.
- insert([
- 'name' => Str::random(10),
- 'email' => Str::random(10).'@example.com',
- 'password' => Hash::make('password'),
- ]);
- }
+ DB::table('users')->insert([
+ 'name' => Str::random(10),
+ 'email' => Str::random(10).'@example.com',
+ 'password' => Hash::make('password'),
+ ]);
}
+}
+```
> [!NOTE]
> You may type-hint any dependencies you need within the `run` method's signature. They will automatically be resolved via the Laravel [service container](/docs/{{version}}/container).
diff --git a/validation.md b/validation.md
index 43d71477dab..3bdca710bbf 100644
--- a/validation.md
+++ b/validation.md
@@ -300,28 +300,30 @@ Below, you can review an example of the JSON response format for validation erro
### Creating Form Requests
-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:
-
-```shell
-php artisan make:request StorePostRequest
-```
+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.
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`.
-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:
+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.
- *
- * @return array|string>
- */
- public function rules(): array
- {
- return [
- 'title' => 'required|unique:posts|max:255',
- 'body' => 'required',
- ];
- }
+```shell tab=Creation
+php artisan make:request StorePostRequest
+```
+
+```php tab=Definition filenam=app/Http/Requests/StorePostRequest.php
+/**
+ * Get the validation rules that apply to the request.
+ *
+ * @return array|string>
+ */
+public function rules(): array
+{
+ return [
+ 'title' => 'required|unique:posts|max:255',
+ 'body' => 'required',
+ ];
+}
+```
> [!NOTE]
> 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).
@@ -2247,41 +2249,45 @@ Occasionally, you may want to attach additional validation rules to your default
### 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. If this directory does not exist, Laravel will create it when you execute the Artisan command to create your rule:
+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.
-```shell
+Once the rule has been created, we are ready to define its behavior. A rule object contains a single method: `validate`. This method receives the attribute name, its value, and a callback that should be invoked on failure with the validation error message.
+
+Once the rule has been defined, you may attach it to a validator by passing an instance of the rule object with your other validation rules.
+
+```shell tab=Creation
php artisan make:rule Uppercase
```
-Once the rule has been created, we are ready to define its behavior. A rule object contains a single method: `validate`. This method receives the attribute name, its value, and a callback that should be invoked on failure with the validation error message:
-
- validate([
- 'name' => ['required', 'string', new Uppercase],
- ]);
+$request->validate([
+ 'name' => ['required', 'string', new Uppercase],
+]);
+```
#### Translating Validation Messages
diff --git a/views.md b/views.md
index 6f9d3fc2ae2..993711923a2 100644
--- a/views.md
+++ b/views.md
@@ -48,19 +48,21 @@ Our Breeze and Jetstream [starter kits](/docs/{{version}}/starter-kits) give you
## Creating and Rendering Views
-You may create a view by placing a file with the `.blade.php` extension in your application's `resources/views` directory or by using the `make:view` Artisan command:
+You may create a view by placing a file with the `.blade.php` extension in your application's `resources/views` directory or by using the `make:view` Artisan command.
-```shell
+Once you have created a view, you may return it from one of your application's routes or controllers using the global `view` helper.
+
+```shell tab=Creation
php artisan make:view greeting
```
-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.
-
-Once you have created a view, you may return it from one of your application's routes or controllers using the global `view` helper:
+```php tab=Usage filename=routes/web.php
+Route::get('/', function () {
+ return view('greeting', ['name' => 'James']);
+});
+```
- Route::get('/', function () {
- return view('greeting', ['name' => 'James']);
- });
+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.
Views may also be returned using the `View` facade: